@mastra/duckdb 1.6.4 → 1.7.0-alpha.1

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.
Files changed (30) hide show
  1. package/dist/{db-TBEcMD49.cjs → db-CJBcanx4.cjs} +27 -1
  2. package/dist/db-CJBcanx4.cjs.map +1 -0
  3. package/dist/{db-Dl0fY488.js → db-b2jufH0V.js} +27 -1
  4. package/dist/db-b2jufH0V.js.map +1 -0
  5. package/dist/docs/SKILL.md +1 -1
  6. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  7. package/dist/index.cjs +12 -4
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.js +12 -4
  10. package/dist/index.js.map +1 -1
  11. package/dist/{observability-CT5eidI9.js → observability-Bt92lKRG.js} +396 -8
  12. package/dist/observability-Bt92lKRG.js.map +1 -0
  13. package/dist/{observability-CYOty2wB.cjs → observability-yLoG0tl3.cjs} +418 -8
  14. package/dist/observability-yLoG0tl3.cjs.map +1 -0
  15. package/dist/storage/db/index.d.ts +5 -0
  16. package/dist/storage/db/index.d.ts.map +1 -1
  17. package/dist/storage/domains/observability/index.d.ts +3 -2
  18. package/dist/storage/domains/observability/index.d.ts.map +1 -1
  19. package/dist/storage/domains/observability/scores.d.ts.map +1 -1
  20. package/dist/storage/domains/observability/trace-query.d.ts +9 -0
  21. package/dist/storage/domains/observability/trace-query.d.ts.map +1 -0
  22. package/dist/storage/domains/observability/tracing.d.ts +7 -1
  23. package/dist/storage/domains/observability/tracing.d.ts.map +1 -1
  24. package/dist/storage/index.d.ts +1 -0
  25. package/dist/storage/index.d.ts.map +1 -1
  26. package/package.json +3 -3
  27. package/dist/db-Dl0fY488.js.map +0 -1
  28. package/dist/db-TBEcMD49.cjs.map +0 -1
  29. package/dist/observability-CT5eidI9.js.map +0 -1
  30. package/dist/observability-CYOty2wB.cjs.map +0 -1
@@ -1,5 +1,6 @@
1
- import { t as DuckDBConnection } from "./db-Dl0fY488.js";
1
+ import { t as DuckDBConnection } from "./db-b2jufH0V.js";
2
2
  import { ErrorCategory, ErrorDomain, MastraError } from "@mastra/core/error";
3
+ import * as coreStorage from "@mastra/core/storage";
3
4
  import { BRANCH_SPAN_TYPES, METRIC_DISTINCT_COLUMNS, ObservabilityStorage, buildInputPreview, computeTraceStatus, createStorageErrorId, feedbackRecordSchema, listBranchesArgsSchema, listFeedbackArgsSchema, listLogsArgsSchema, listMetricsArgsSchema, listScoresArgsSchema, listTracesArgsSchema, toTraceSpans } from "@mastra/core/storage";
4
5
  import { coreFeatures } from "@mastra/core/features";
5
6
  import { EntityType } from "@mastra/core/observability";
@@ -2070,6 +2071,43 @@ async function migrateSignalTables(db, logger) {
2070
2071
  }
2071
2072
  //#endregion
2072
2073
  //#region src/storage/domains/observability/scores.ts
2074
+ const SCORE_UPSERT_CLAUSE = `ON CONFLICT (scoreId) DO UPDATE SET ${[
2075
+ "timestamp",
2076
+ "traceId",
2077
+ "spanId",
2078
+ "experimentId",
2079
+ "scoreTraceId",
2080
+ "entityType",
2081
+ "entityId",
2082
+ "entityName",
2083
+ "entityVersionId",
2084
+ "parentEntityVersionId",
2085
+ "parentEntityType",
2086
+ "parentEntityId",
2087
+ "parentEntityName",
2088
+ "rootEntityVersionId",
2089
+ "rootEntityType",
2090
+ "rootEntityId",
2091
+ "rootEntityName",
2092
+ "userId",
2093
+ "organizationId",
2094
+ "resourceId",
2095
+ "runId",
2096
+ "sessionId",
2097
+ "threadId",
2098
+ "requestId",
2099
+ "environment",
2100
+ "executionSource",
2101
+ "serviceName",
2102
+ "scorerId",
2103
+ "scorerVersion",
2104
+ "scoreSource",
2105
+ "score",
2106
+ "reason",
2107
+ "tags",
2108
+ "metadata",
2109
+ "scope"
2110
+ ].map((column) => `${column} = excluded.${column}`).join(", ")}`;
2073
2111
  const SCORE_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
2074
2112
  "timestamp",
2075
2113
  "traceId",
@@ -2278,7 +2316,7 @@ async function createScore(db, args) {
2278
2316
  jsonV(s.metadata),
2279
2317
  jsonV(s.scope ?? null)
2280
2318
  ].join(", ")})
2281
- ON CONFLICT DO NOTHING`);
2319
+ ${SCORE_UPSERT_CLAUSE}`);
2282
2320
  }
2283
2321
  /** Insert multiple score events in a single statement. */
2284
2322
  async function batchCreateScores(db, args) {
@@ -2333,7 +2371,7 @@ async function batchCreateScores(db, args) {
2333
2371
  scorerId, scorerVersion, scoreSource, score, reason, tags, metadata, scope
2334
2372
  )
2335
2373
  VALUES ${tuples.join(",\n ")}
2336
- ON CONFLICT DO NOTHING`);
2374
+ ${SCORE_UPSERT_CLAUSE}`);
2337
2375
  }
2338
2376
  /** Query score events with filtering, ordering, and pagination. */
2339
2377
  async function listScores(db, args) {
@@ -2519,6 +2557,323 @@ async function getScorePercentiles(db, args) {
2519
2557
  return { series };
2520
2558
  }
2521
2559
  //#endregion
2560
+ //#region src/storage/domains/observability/trace-query.ts
2561
+ const TRACE_STATUS_SQL = `CASE WHEN r.error IS NOT NULL THEN 'error' ELSE 'success' END`;
2562
+ const TRACE_FIELDS = {
2563
+ traceId: {
2564
+ sql: "r.traceId",
2565
+ parameterType: "scalar"
2566
+ },
2567
+ threadId: {
2568
+ sql: "r.threadId",
2569
+ parameterType: "scalar"
2570
+ },
2571
+ resourceId: {
2572
+ sql: "r.resourceId",
2573
+ parameterType: "scalar"
2574
+ },
2575
+ startedAt: {
2576
+ sql: "r.startedAt",
2577
+ parameterType: "timestamp"
2578
+ },
2579
+ endedAt: {
2580
+ sql: "r.endedAt",
2581
+ parameterType: "timestamp"
2582
+ },
2583
+ entityName: {
2584
+ sql: "r.entityName",
2585
+ parameterType: "scalar"
2586
+ },
2587
+ entityType: {
2588
+ sql: "r.entityType",
2589
+ parameterType: "scalar"
2590
+ },
2591
+ environment: {
2592
+ sql: "r.environment",
2593
+ parameterType: "scalar"
2594
+ },
2595
+ status: {
2596
+ sql: TRACE_STATUS_SQL,
2597
+ parameterType: "scalar"
2598
+ }
2599
+ };
2600
+ const SPAN_FIELDS = {
2601
+ spanType: {
2602
+ sql: "s.spanType",
2603
+ parameterType: "scalar"
2604
+ },
2605
+ error: {
2606
+ sql: "s.error",
2607
+ parameterType: "scalar"
2608
+ }
2609
+ };
2610
+ const SCORE_FIELDS = {
2611
+ scorerId: {
2612
+ sql: "s.scorerId",
2613
+ parameterType: "scalar"
2614
+ },
2615
+ score: {
2616
+ sql: "s.score",
2617
+ parameterType: "scalar"
2618
+ }
2619
+ };
2620
+ const TRACE_SELECT = `
2621
+ r.traceId AS traceId,
2622
+ r.spanId AS rootSpanId,
2623
+ r.threadId AS threadId,
2624
+ r.resourceId AS resourceId,
2625
+ r.startedAt AS startedAt,
2626
+ r.endedAt AS endedAt,
2627
+ r.entityName AS entityName,
2628
+ r.entityType AS entityType,
2629
+ r.environment AS environment,
2630
+ ${TRACE_STATUS_SQL} AS status`;
2631
+ function fieldDefinition(registry, field) {
2632
+ const definition = registry[field];
2633
+ if (definition === void 0) throw new Error(`Unsupported trusted trace-query field: ${field}`);
2634
+ return definition;
2635
+ }
2636
+ function parameterSql(type) {
2637
+ return type === "timestamp" ? "CAST(? AS TIMESTAMP)" : "?";
2638
+ }
2639
+ function compileScalarPredicate(predicate, registry) {
2640
+ if (predicate.type === "boolean") {
2641
+ const values = [];
2642
+ return {
2643
+ sql: predicate.args.map((arg) => {
2644
+ const compiled = compileScalarPredicate(arg, registry);
2645
+ values.push(...compiled.values);
2646
+ return `(${compiled.sql})`;
2647
+ }).join(predicate.operator === "and" ? " AND " : " OR "),
2648
+ values
2649
+ };
2650
+ }
2651
+ if (predicate.type === "not") {
2652
+ const compiled = compileScalarPredicate(predicate.arg, registry);
2653
+ return {
2654
+ sql: `NOT (${compiled.sql})`,
2655
+ values: compiled.values
2656
+ };
2657
+ }
2658
+ const field = fieldDefinition(registry, predicate.field);
2659
+ if (predicate.type === "presence") return {
2660
+ sql: `${field.sql} IS ${predicate.operator === "exists" ? "NOT " : ""}NULL`,
2661
+ values: []
2662
+ };
2663
+ if (predicate.type === "membership") {
2664
+ const list = predicate.values.map(() => parameterSql(field.parameterType)).join(", ");
2665
+ if (predicate.operator === "in") return {
2666
+ sql: `${field.sql} IS NOT NULL AND ${field.sql} IN (${list})`,
2667
+ values: predicate.values
2668
+ };
2669
+ return {
2670
+ sql: `${field.sql} IS NULL OR ${field.sql} NOT IN (${list})`,
2671
+ values: predicate.values
2672
+ };
2673
+ }
2674
+ const parameter = parameterSql(field.parameterType);
2675
+ const operators = {
2676
+ lt: "<",
2677
+ lte: "<=",
2678
+ gt: ">",
2679
+ gte: ">="
2680
+ };
2681
+ if (predicate.operator === "eq") return {
2682
+ sql: `${field.sql} IS NOT DISTINCT FROM ${parameter}`,
2683
+ values: [predicate.value]
2684
+ };
2685
+ if (predicate.operator === "ne") return {
2686
+ sql: `${field.sql} IS DISTINCT FROM ${parameter}`,
2687
+ values: [predicate.value]
2688
+ };
2689
+ const operator = operators[predicate.operator];
2690
+ if (operator === void 0) throw new Error(`Unsupported trusted trace-query operator: ${predicate.operator}`);
2691
+ return {
2692
+ sql: `${field.sql} IS NOT NULL AND ${field.sql} ${operator} ${parameter}`,
2693
+ values: [predicate.value]
2694
+ };
2695
+ }
2696
+ function compilePredicate(predicate) {
2697
+ if (predicate.type === "relation") {
2698
+ const registry = predicate.collection === "spans" ? SPAN_FIELDS : SCORE_FIELDS;
2699
+ const compiled = compileScalarPredicate(predicate.predicate, registry);
2700
+ const existence = `EXISTS (
2701
+ SELECT 1 FROM ${predicate.collection === "spans" ? "current_spans" : "current_scores"} s
2702
+ WHERE s.traceId IS NOT NULL
2703
+ AND s.traceId = r.traceId
2704
+ AND (${compiled.sql})
2705
+ )`;
2706
+ return {
2707
+ sql: predicate.quantifier === "some" ? existence : `NOT ${existence}`,
2708
+ values: compiled.values
2709
+ };
2710
+ }
2711
+ if (predicate.type === "boolean") {
2712
+ const values = [];
2713
+ return {
2714
+ sql: predicate.args.map((arg) => {
2715
+ const compiled = compilePredicate(arg);
2716
+ values.push(...compiled.values);
2717
+ return `(${compiled.sql})`;
2718
+ }).join(predicate.operator === "and" ? " AND " : " OR "),
2719
+ values
2720
+ };
2721
+ }
2722
+ if (predicate.type === "not") {
2723
+ const compiled = compilePredicate(predicate.arg);
2724
+ return {
2725
+ sql: `NOT (${compiled.sql})`,
2726
+ values: compiled.values
2727
+ };
2728
+ }
2729
+ return compileScalarPredicate(predicate, TRACE_FIELDS);
2730
+ }
2731
+ function collectRelatedCollections(predicate, collections = /* @__PURE__ */ new Set()) {
2732
+ if (!predicate) return collections;
2733
+ if (predicate.type === "relation") collections.add(predicate.collection);
2734
+ else if (predicate.type === "boolean") for (const arg of predicate.args) collectRelatedCollections(arg, collections);
2735
+ else if (predicate.type === "not") collectRelatedCollections(predicate.arg, collections);
2736
+ return collections;
2737
+ }
2738
+ function compileDuckDBTraceQuery(plan) {
2739
+ const values = [plan.timeRange.from, plan.timeRange.to];
2740
+ const conditions = [
2741
+ `r.endedAt IS NOT NULL`,
2742
+ `r.startedAt >= CAST(? AS TIMESTAMP)`,
2743
+ `r.startedAt < CAST(? AS TIMESTAMP)`
2744
+ ];
2745
+ if (plan.where) {
2746
+ const predicate = compilePredicate(plan.where);
2747
+ conditions.push(`(${predicate.sql})`);
2748
+ values.push(...predicate.values);
2749
+ }
2750
+ const relatedCollections = collectRelatedCollections(plan.where);
2751
+ const ctes = [
2752
+ `root_events AS (
2753
+ SELECT
2754
+ *,
2755
+ CASE
2756
+ WHEN eventType = 'start' THEN timestamp
2757
+ ELSE lag(timestamp) OVER (PARTITION BY traceId, spanId ORDER BY cursorId)
2758
+ END AS startedAt
2759
+ FROM span_events
2760
+ WHERE parentSpanId IS NULL
2761
+ )`,
2762
+ `current_roots AS (
2763
+ SELECT * EXCLUDE (rootRank)
2764
+ FROM (
2765
+ SELECT *, row_number() OVER (PARTITION BY traceId ORDER BY cursorId DESC) AS rootRank
2766
+ FROM root_events
2767
+ )
2768
+ WHERE rootRank = 1
2769
+ )`,
2770
+ `root_scope AS (
2771
+ SELECT *
2772
+ FROM current_roots r
2773
+ WHERE r.endedAt IS NOT NULL
2774
+ AND r.startedAt >= CAST(? AS TIMESTAMP)
2775
+ AND r.startedAt < CAST(? AS TIMESTAMP)
2776
+ )`
2777
+ ];
2778
+ if (relatedCollections.has("spans")) ctes.push(`current_spans AS (
2779
+ SELECT * EXCLUDE (currentRank)
2780
+ FROM (
2781
+ SELECT
2782
+ e.*,
2783
+ row_number() OVER (
2784
+ PARTITION BY e.traceId, e.spanId
2785
+ ORDER BY CASE WHEN e.endedAt IS NULL THEN 1 ELSE 0 END ASC, e.cursorId DESC
2786
+ ) AS currentRank
2787
+ FROM span_events e
2788
+ INNER JOIN root_scope roots ON roots.traceId = e.traceId
2789
+ )
2790
+ WHERE currentRank = 1
2791
+ )`);
2792
+ if (relatedCollections.has("scores")) ctes.push(`current_scores AS (
2793
+ SELECT s.*
2794
+ FROM score_events s
2795
+ INNER JOIN root_scope roots ON roots.traceId = s.traceId
2796
+ )`);
2797
+ ctes.push(`candidates AS (
2798
+ SELECT ${TRACE_SELECT}
2799
+ FROM root_scope r
2800
+ WHERE ${conditions.slice(3).join("\n AND ") || "TRUE"}
2801
+ )`);
2802
+ const candidates = `WITH ${ctes.join(",\n ")}`;
2803
+ if (plan.result === "groups") {
2804
+ const pageCondition = plan.cursor ? `AND threadId > ?` : "";
2805
+ if (plan.cursor) values.push(plan.cursor.threadId);
2806
+ values.push(plan.limit + 1);
2807
+ return {
2808
+ sql: `${candidates}
2809
+ SELECT threadId
2810
+ FROM candidates
2811
+ WHERE threadId IS NOT NULL ${pageCondition}
2812
+ GROUP BY threadId
2813
+ ORDER BY threadId ASC
2814
+ LIMIT ?`,
2815
+ values
2816
+ };
2817
+ }
2818
+ const orderField = plan.orderBy.field;
2819
+ const direction = plan.orderBy.direction === "asc" ? "ASC" : "DESC";
2820
+ let pageCondition = "";
2821
+ if (plan.cursor) {
2822
+ pageCondition = `WHERE (${orderField} ${plan.orderBy.direction === "asc" ? ">" : "<"} CAST(? AS TIMESTAMP) OR (${orderField} = CAST(? AS TIMESTAMP) AND traceId > ?))`;
2823
+ values.push(plan.cursor.sortValue, plan.cursor.sortValue, plan.cursor.traceId);
2824
+ }
2825
+ values.push(plan.limit + 1);
2826
+ return {
2827
+ sql: `${candidates}
2828
+ SELECT *
2829
+ FROM candidates
2830
+ ${pageCondition}
2831
+ ORDER BY ${orderField} ${direction}, traceId ASC
2832
+ LIMIT ?`,
2833
+ values
2834
+ };
2835
+ }
2836
+ function asIsoTimestamp(value) {
2837
+ return value instanceof Date ? value.toISOString() : new Date(value).toISOString();
2838
+ }
2839
+ async function queryTraces(db, plan) {
2840
+ const query = compileDuckDBTraceQuery(plan);
2841
+ const rows = await db.query(query.sql, query.values);
2842
+ const visibleRows = rows.slice(0, plan.limit);
2843
+ if (plan.result === "groups") {
2844
+ const groups = visibleRows.map((row) => ({ threadId: String(row.threadId) }));
2845
+ const last = groups.at(-1);
2846
+ return coreStorage.traceQueryResponseSchema.parse({
2847
+ groups,
2848
+ page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
2849
+ result: "groups",
2850
+ threadId: last.threadId
2851
+ }) : null }
2852
+ });
2853
+ }
2854
+ const traces = visibleRows.map((row) => ({
2855
+ traceId: String(row.traceId),
2856
+ rootSpanId: String(row.rootSpanId),
2857
+ threadId: row.threadId == null ? null : String(row.threadId),
2858
+ resourceId: row.resourceId == null ? null : String(row.resourceId),
2859
+ startedAt: asIsoTimestamp(row.startedAt),
2860
+ endedAt: asIsoTimestamp(row.endedAt),
2861
+ entityName: row.entityName == null ? null : String(row.entityName),
2862
+ entityType: row.entityType == null ? null : String(row.entityType),
2863
+ environment: row.environment == null ? null : String(row.environment),
2864
+ status: row.status
2865
+ }));
2866
+ const last = traces.at(-1);
2867
+ return coreStorage.traceQueryResponseSchema.parse({
2868
+ traces,
2869
+ page: { next: rows.length > plan.limit && last ? coreStorage.encodeTraceQueryCursor(plan, {
2870
+ result: "traces",
2871
+ sortValue: last[plan.orderBy.field],
2872
+ traceId: last.traceId
2873
+ }) : null }
2874
+ });
2875
+ }
2876
+ //#endregion
2522
2877
  //#region src/storage/domains/observability/tracing.ts
2523
2878
  const COLUMNS_SQL = [
2524
2879
  "eventType",
@@ -3015,11 +3370,36 @@ async function batchCreateSpans(db, args) {
3015
3370
  return events;
3016
3371
  }));
3017
3372
  }
3018
- /** Delete all span events for the given trace IDs. */
3373
+ /**
3374
+ * Delete all span events for the given trace IDs, cascading to trace-linked
3375
+ * signal events (metrics, logs, scores, feedback). Signal rows with a NULL
3376
+ * traceId are never affected. When the optional tenant scope
3377
+ * (`organizationId` / `resourceId`) is set, every DELETE additionally
3378
+ * requires the row's tenant columns to match.
3379
+ */
3019
3380
  async function batchDeleteTraces(db, args) {
3020
3381
  if (args.traceIds.length === 0) return;
3021
3382
  const placeholders = args.traceIds.map(() => "?").join(", ");
3022
- await db.execute(`DELETE FROM span_events WHERE traceId IN (${placeholders})`, args.traceIds);
3383
+ const params = [...args.traceIds];
3384
+ let scopeCondition = "";
3385
+ if (args.organizationId !== void 0) {
3386
+ scopeCondition += ` AND organizationId = ?`;
3387
+ params.push(args.organizationId);
3388
+ }
3389
+ if (args.resourceId !== void 0) {
3390
+ scopeCondition += ` AND resourceId = ?`;
3391
+ params.push(args.resourceId);
3392
+ }
3393
+ await db.executeTransaction([
3394
+ "span_events",
3395
+ "metric_events",
3396
+ "log_events",
3397
+ "score_events",
3398
+ "feedback_events"
3399
+ ].map((table) => ({
3400
+ sql: `DELETE FROM ${table} WHERE traceId IN (${placeholders})${scopeCondition}`,
3401
+ params
3402
+ })));
3023
3403
  }
3024
3404
  /** Reconstruct a single span from its event history. */
3025
3405
  async function getSpan(db, args) {
@@ -3717,11 +4097,16 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
3717
4097
  };
3718
4098
  }
3719
4099
  getFeatures() {
3720
- if (!deltaPollingFeatureEnabled()) return ["metrics", "logs"];
4100
+ if (!deltaPollingFeatureEnabled()) return [
4101
+ "metrics",
4102
+ "logs",
4103
+ "trace-query"
4104
+ ];
3721
4105
  return [
3722
4106
  "metrics",
3723
4107
  "logs",
3724
- "delta-polling"
4108
+ "delta-polling",
4109
+ "trace-query"
3725
4110
  ];
3726
4111
  }
3727
4112
  async createSpan(args) {
@@ -3751,6 +4136,9 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
3751
4136
  async listTraces(args) {
3752
4137
  return listTraces(this.db, args);
3753
4138
  }
4139
+ async queryTraces(plan) {
4140
+ return queryTraces(this.db, plan);
4141
+ }
3754
4142
  async listTracesLight(args) {
3755
4143
  if (args.mode === "delta") return super.listTracesLight(args);
3756
4144
  return listTracesLight(this.db, args);
@@ -3858,4 +4246,4 @@ var ObservabilityStorageDuckDB = class extends ObservabilityStorage {
3858
4246
  //#endregion
3859
4247
  export { ObservabilityStorageDuckDB };
3860
4248
 
3861
- //# sourceMappingURL=observability-CT5eidI9.js.map
4249
+ //# sourceMappingURL=observability-Bt92lKRG.js.map