@mastra/duckdb 1.8.0 → 1.9.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.
@@ -290,6 +290,8 @@ CREATE TABLE IF NOT EXISTS feedback_events (
290
290
  feedbackSource VARCHAR NOT NULL,
291
291
  feedbackType VARCHAR NOT NULL,
292
292
  value VARCHAR NOT NULL,
293
+ valueString VARCHAR,
294
+ valueNumber DOUBLE,
293
295
  comment VARCHAR,
294
296
 
295
297
  -- JSON fields
@@ -425,6 +427,8 @@ const ALL_MIGRATIONS = [
425
427
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS scope JSON`,
426
428
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS source VARCHAR`,
427
429
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS feedbackSource VARCHAR`,
430
+ `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS valueString VARCHAR`,
431
+ `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS valueNumber DOUBLE`,
428
432
  `ALTER TABLE feedback_events ADD COLUMN IF NOT EXISTS reviewStatus VARCHAR DEFAULT 'needs-review'`,
429
433
  `ALTER TABLE feedback_events ALTER COLUMN traceId DROP NOT NULL`
430
434
  ];
@@ -779,6 +783,8 @@ const FEEDBACK_UPSERT_CLAUSE = `ON CONFLICT (feedbackId) DO UPDATE SET ${[
779
783
  "feedbackSource",
780
784
  "feedbackType",
781
785
  "value",
786
+ "valueString",
787
+ "valueNumber",
782
788
  "comment",
783
789
  "tags",
784
790
  "metadata",
@@ -818,7 +824,7 @@ const FEEDBACK_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
818
824
  "value",
819
825
  "comment"
820
826
  ]);
821
- function getAggregationSql$2(aggregation, measure = "TRY_CAST(value AS DOUBLE)") {
827
+ function getAggregationSql$2(aggregation, measure = "valueNumber") {
822
828
  switch (aggregation) {
823
829
  case "sum": return `SUM(${measure})`;
824
830
  case "avg": return `AVG(${measure})`;
@@ -858,7 +864,7 @@ function buildFeedbackWhereClause(args, includeNumericGuard = false) {
858
864
  conditions.push(filterClause.replace("WHERE ", ""));
859
865
  params.push(...filterParams);
860
866
  }
861
- if (includeNumericGuard) conditions.push("TRY_CAST(value AS DOUBLE) IS NOT NULL");
867
+ if (includeNumericGuard) conditions.push("valueNumber IS NOT NULL");
862
868
  return {
863
869
  clause: `WHERE ${conditions.join(" AND ")}`,
864
870
  params
@@ -879,11 +885,12 @@ function resolveFeedbackGroupBy(groupBy) {
879
885
  function toSeriesName$1(values) {
880
886
  return values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|");
881
887
  }
888
+ function readFeedbackValue(row) {
889
+ if (row.valueNumber != null) return Number(row.valueNumber);
890
+ if (row.valueString != null) return String(row.valueString);
891
+ return String(row.value);
892
+ }
882
893
  function rowToFeedbackRecord(row) {
883
- const rawValue = row.value;
884
- let value = rawValue;
885
- const numValue = Number(rawValue);
886
- if (!isNaN(numValue)) value = numValue;
887
894
  return feedbackRecordSchema.parse({
888
895
  feedbackId: row.feedbackId,
889
896
  timestamp: toDate(row.timestamp),
@@ -918,7 +925,7 @@ function rowToFeedbackRecord(row) {
918
925
  source: row.feedbackSource,
919
926
  feedbackSource: row.feedbackSource,
920
927
  feedbackType: row.feedbackType,
921
- value,
928
+ value: readFeedbackValue(row),
922
929
  comment: row.comment ?? null,
923
930
  tags: parseJsonArray(row.tags),
924
931
  metadata: parseJson(row.metadata),
@@ -958,7 +965,7 @@ async function createFeedback(db, args) {
958
965
  feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
959
966
  entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
960
967
  userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
961
- feedbackUserId, sourceId, reviewStatus, feedbackSource, feedbackType, value, comment, tags, metadata, scope
968
+ feedbackUserId, sourceId, reviewStatus, feedbackSource, feedbackType, value, valueString, valueNumber, comment, tags, metadata, scope
962
969
  )
963
970
  VALUES (${[
964
971
  v(f.feedbackId),
@@ -995,6 +1002,8 @@ async function createFeedback(db, args) {
995
1002
  v(feedbackSource),
996
1003
  v(f.feedbackType),
997
1004
  v(String(f.value)),
1005
+ v(typeof f.value === "string" ? f.value : null),
1006
+ v(typeof f.value === "number" ? f.value : null),
998
1007
  v(f.comment ?? null),
999
1008
  jsonV(f.tags ?? null),
1000
1009
  jsonV(f.metadata),
@@ -1044,6 +1053,8 @@ async function batchCreateFeedback(db, args) {
1044
1053
  v(feedbackSource),
1045
1054
  v(legacyFeedback.feedbackType),
1046
1055
  v(String(legacyFeedback.value)),
1056
+ v(typeof legacyFeedback.value === "string" ? legacyFeedback.value : null),
1057
+ v(typeof legacyFeedback.value === "number" ? legacyFeedback.value : null),
1047
1058
  v(legacyFeedback.comment ?? null),
1048
1059
  jsonV(legacyFeedback.tags ?? null),
1049
1060
  jsonV(legacyFeedback.metadata),
@@ -1054,7 +1065,7 @@ async function batchCreateFeedback(db, args) {
1054
1065
  feedbackId, timestamp, cursorId, traceId, spanId, experimentId,
1055
1066
  entityType, entityId, entityName, entityVersionId, parentEntityVersionId, parentEntityType, parentEntityId, parentEntityName, rootEntityVersionId, rootEntityType, rootEntityId, rootEntityName,
1056
1067
  userId, organizationId, resourceId, runId, sessionId, threadId, requestId, environment, executionSource, serviceName,
1057
- feedbackUserId, sourceId, reviewStatus, feedbackSource, feedbackType, value, comment, tags, metadata, scope
1068
+ feedbackUserId, sourceId, reviewStatus, feedbackSource, feedbackType, value, valueString, valueNumber, comment, tags, metadata, scope
1058
1069
  )
1059
1070
  VALUES ${tuples.join(",\n ")}
1060
1071
  ${FEEDBACK_UPSERT_CLAUSE}`);
@@ -1251,7 +1262,7 @@ async function getFeedbackPercentiles(db, args) {
1251
1262
  for (const percentile of percentiles) {
1252
1263
  const rows = await db.query(`
1253
1264
  SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1254
- percentile_cont(${percentile}) WITHIN GROUP (ORDER BY TRY_CAST(value AS DOUBLE)) AS pvalue
1265
+ percentile_cont(${percentile}) WITHIN GROUP (ORDER BY valueNumber) AS pvalue
1255
1266
  FROM feedback_events ${clause}
1256
1267
  GROUP BY bucket
1257
1268
  ORDER BY bucket
@@ -2943,7 +2954,7 @@ function compileFeedbackScalarPredicate(predicate) {
2943
2954
  values: []
2944
2955
  };
2945
2956
  return compileScalarPredicate(predicate, { value: {
2946
- sql: typeof (predicate.type === "membership" ? predicate.values[0] : predicate.value) === "number" ? "TRY_CAST(s.value AS DOUBLE)" : "s.value",
2957
+ sql: typeof (predicate.type === "membership" ? predicate.values[0] : predicate.value) === "number" ? "s.valueNumber" : "s.valueString",
2947
2958
  parameterType: "scalar"
2948
2959
  } });
2949
2960
  }
@@ -2981,6 +2992,36 @@ function compilePredicate(predicate) {
2981
2992
  }
2982
2993
  return compileScalarPredicate(predicate, TRACE_FIELDS, true);
2983
2994
  }
2995
+ function compileThreadPredicate(predicate) {
2996
+ if (predicate.type === "relation") {
2997
+ const compiled = compilePredicate(predicate.predicate);
2998
+ const existence = `EXISTS (
2999
+ SELECT 1 FROM eligible_roots r
3000
+ WHERE r.threadId = t.threadId
3001
+ AND (${compiled.sql})
3002
+ )`;
3003
+ return {
3004
+ sql: predicate.quantifier === "some" ? existence : `NOT ${existence}`,
3005
+ values: compiled.values
3006
+ };
3007
+ }
3008
+ if (predicate.type === "boolean") {
3009
+ const values = [];
3010
+ return {
3011
+ sql: predicate.args.map((arg) => {
3012
+ const compiled = compileThreadPredicate(arg);
3013
+ values.push(...compiled.values);
3014
+ return `(${compiled.sql})`;
3015
+ }).join(predicate.operator === "and" ? " AND " : " OR "),
3016
+ values
3017
+ };
3018
+ }
3019
+ const compiled = compileThreadPredicate(predicate.arg);
3020
+ return {
3021
+ sql: `NOT (${compiled.sql})`,
3022
+ values: compiled.values
3023
+ };
3024
+ }
2984
3025
  function collectRelatedCollections(predicate, collections = /* @__PURE__ */ new Set()) {
2985
3026
  if (!predicate) return collections;
2986
3027
  if (predicate.type === "relation") collections.add(predicate.collection);
@@ -2988,19 +3029,14 @@ function collectRelatedCollections(predicate, collections = /* @__PURE__ */ new
2988
3029
  else if (predicate.type === "not") collectRelatedCollections(predicate.arg, collections);
2989
3030
  return collections;
2990
3031
  }
2991
- function compileDuckDBTraceQuery(plan) {
2992
- const values = [plan.timeRange.from, plan.timeRange.to];
2993
- const conditions = [
2994
- `r.endedAt IS NOT NULL`,
2995
- `r.startedAt >= CAST(? AS TIMESTAMP)`,
2996
- `r.startedAt < CAST(? AS TIMESTAMP)`
2997
- ];
2998
- if (plan.where) {
2999
- const predicate = compilePredicate(plan.where);
3000
- conditions.push(`(${predicate.sql})`);
3001
- values.push(...predicate.values);
3002
- }
3003
- const relatedCollections = collectRelatedCollections(plan.where);
3032
+ function collectThreadRelatedCollections(predicate, collections) {
3033
+ if (!predicate) return collections;
3034
+ if (predicate.type === "relation") collectRelatedCollections(predicate.predicate, collections);
3035
+ else if (predicate.type === "boolean") for (const arg of predicate.args) collectThreadRelatedCollections(arg, collections);
3036
+ else collectThreadRelatedCollections(predicate.arg, collections);
3037
+ return collections;
3038
+ }
3039
+ function compileDuckDBTraceScope(relatedCollections) {
3004
3040
  const ctes = [
3005
3041
  `root_events AS (
3006
3042
  SELECT
@@ -3077,6 +3113,21 @@ function compileDuckDBTraceQuery(plan) {
3077
3113
  FROM feedback_events f
3078
3114
  INNER JOIN root_scope roots ON roots.traceId = f.traceId
3079
3115
  )`);
3116
+ return ctes;
3117
+ }
3118
+ function compileDuckDBTraceQuery(plan) {
3119
+ const values = [plan.timeRange.from, plan.timeRange.to];
3120
+ const conditions = [
3121
+ `r.endedAt IS NOT NULL`,
3122
+ `r.startedAt >= CAST(? AS TIMESTAMP)`,
3123
+ `r.startedAt < CAST(? AS TIMESTAMP)`
3124
+ ];
3125
+ if (plan.where) {
3126
+ const predicate = compilePredicate(plan.where);
3127
+ conditions.push(`(${predicate.sql})`);
3128
+ values.push(...predicate.values);
3129
+ }
3130
+ const ctes = compileDuckDBTraceScope(collectRelatedCollections(plan.where));
3080
3131
  ctes.push(`candidates AS (
3081
3132
  SELECT ${TRACE_SELECT}
3082
3133
  FROM root_scope r
@@ -3116,6 +3167,52 @@ LIMIT ?`,
3116
3167
  values
3117
3168
  };
3118
3169
  }
3170
+ function compileDuckDBThreadQuery(plan) {
3171
+ const values = [plan.traces.timeRange.from, plan.traces.timeRange.to];
3172
+ const relatedCollections = collectRelatedCollections(plan.traces.where);
3173
+ collectThreadRelatedCollections(plan.where, relatedCollections);
3174
+ const ctes = compileDuckDBTraceScope(relatedCollections);
3175
+ let eligibilitySql = "TRUE";
3176
+ if (plan.traces.where) {
3177
+ const eligibility = compilePredicate(plan.traces.where);
3178
+ eligibilitySql = eligibility.sql;
3179
+ values.push(...eligibility.values);
3180
+ }
3181
+ ctes.push(`eligible_roots AS (
3182
+ SELECT *
3183
+ FROM root_scope r
3184
+ WHERE ${eligibilitySql}
3185
+ )`);
3186
+ ctes.push(`thread_ids AS (
3187
+ SELECT threadId
3188
+ FROM eligible_roots
3189
+ WHERE threadId IS NOT NULL
3190
+ GROUP BY threadId
3191
+ )`);
3192
+ let threadPredicateSql = "TRUE";
3193
+ if (plan.where) {
3194
+ const predicate = compileThreadPredicate(plan.where);
3195
+ threadPredicateSql = predicate.sql;
3196
+ values.push(...predicate.values);
3197
+ }
3198
+ ctes.push(`qualified_threads AS (
3199
+ SELECT t.threadId
3200
+ FROM thread_ids t
3201
+ WHERE ${threadPredicateSql}
3202
+ )`);
3203
+ const pageCondition = plan.cursor ? `WHERE threadId > ?` : "";
3204
+ if (plan.cursor) values.push(plan.cursor.threadId);
3205
+ values.push(plan.limit + 1);
3206
+ return {
3207
+ sql: `WITH ${ctes.join(",\n ")}
3208
+ SELECT threadId
3209
+ FROM qualified_threads
3210
+ ${pageCondition}
3211
+ ORDER BY threadId ASC
3212
+ LIMIT ?`,
3213
+ values
3214
+ };
3215
+ }
3119
3216
  function asIsoTimestamp(value) {
3120
3217
  return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
3121
3218
  }
@@ -3156,6 +3253,19 @@ async function queryTraces(db, plan) {
3156
3253
  }) : null }
3157
3254
  });
3158
3255
  }
3256
+ async function queryThreads(db, plan) {
3257
+ const query = compileDuckDBThreadQuery(plan);
3258
+ const rows = await db.query(query.sql, query.values);
3259
+ const threads = rows.slice(0, plan.limit).map((row) => ({ threadId: String(row.threadId) }));
3260
+ const last = threads.at(-1);
3261
+ return coreStorage.queryThreadsResultSchema.parse({
3262
+ threads,
3263
+ page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
3264
+ result: "threads",
3265
+ threadId: last.threadId
3266
+ }) : null }
3267
+ });
3268
+ }
3159
3269
  //#endregion
3160
3270
  //#region src/storage/domains/observability/tracing.ts
3161
3271
  const COLUMNS_SQL = [
@@ -4395,13 +4505,15 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
4395
4505
  if (!deltaPollingFeatureEnabled()) return [
4396
4506
  "metrics",
4397
4507
  "logs",
4398
- "trace-query"
4508
+ "trace-query",
4509
+ "thread-query"
4399
4510
  ];
4400
4511
  return [
4401
4512
  "metrics",
4402
4513
  "logs",
4403
4514
  "delta-polling",
4404
- "trace-query"
4515
+ "trace-query",
4516
+ "thread-query"
4405
4517
  ];
4406
4518
  }
4407
4519
  async createSpan(args) {
@@ -4434,6 +4546,9 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
4434
4546
  async queryTraces(plan) {
4435
4547
  return queryTraces(this.db, plan);
4436
4548
  }
4549
+ async queryThreads(plan) {
4550
+ return queryThreads(this.db, plan);
4551
+ }
4437
4552
  async listTracesLight(args) {
4438
4553
  if (args.mode === "delta") return super.listTracesLight(args);
4439
4554
  return listTracesLight(this.db, args);
@@ -4547,4 +4662,4 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
4547
4662
  //#endregion
4548
4663
  export { ObservabilityStorageDuckDB };
4549
4664
 
4550
- //# sourceMappingURL=observability-COgD6BOK.js.map
4665
+ //# sourceMappingURL=observability-DEjVUB3O.js.map