@mastra/duckdb 1.1.0-alpha.2 → 1.1.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.
@@ -568,6 +568,187 @@ function parseJsonArray(value) {
568
568
  }
569
569
 
570
570
  // src/storage/domains/observability/feedback.ts
571
+ var FEEDBACK_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
572
+ "timestamp",
573
+ "traceId",
574
+ "spanId",
575
+ "experimentId",
576
+ "entityType",
577
+ "entityId",
578
+ "entityName",
579
+ "parentEntityType",
580
+ "parentEntityId",
581
+ "parentEntityName",
582
+ "rootEntityType",
583
+ "rootEntityId",
584
+ "rootEntityName",
585
+ "userId",
586
+ "organizationId",
587
+ "resourceId",
588
+ "runId",
589
+ "sessionId",
590
+ "threadId",
591
+ "requestId",
592
+ "environment",
593
+ "executionSource",
594
+ "serviceName",
595
+ "feedbackUserId",
596
+ "sourceId",
597
+ "feedbackSource",
598
+ "feedbackType",
599
+ "value",
600
+ "comment"
601
+ ]);
602
+ function getAggregationSql(aggregation, measure = "TRY_CAST(value AS DOUBLE)") {
603
+ switch (aggregation) {
604
+ case "sum":
605
+ return `SUM(${measure})`;
606
+ case "avg":
607
+ return `AVG(${measure})`;
608
+ case "min":
609
+ return `MIN(${measure})`;
610
+ case "max":
611
+ return `MAX(${measure})`;
612
+ case "count":
613
+ return `CAST(COUNT(${measure}) AS DOUBLE)`;
614
+ case "last":
615
+ return `arg_max(${measure}, timestamp)`;
616
+ default:
617
+ return `SUM(${measure})`;
618
+ }
619
+ }
620
+ function getIntervalSql(interval) {
621
+ switch (interval) {
622
+ case "1m":
623
+ return "1 minute";
624
+ case "5m":
625
+ return "5 minutes";
626
+ case "15m":
627
+ return "15 minutes";
628
+ case "1h":
629
+ return "1 hour";
630
+ case "1d":
631
+ return "1 day";
632
+ default:
633
+ return "1 hour";
634
+ }
635
+ }
636
+ function getValidatedPercentiles(percentiles) {
637
+ if (!Array.isArray(percentiles) || percentiles.length === 0) {
638
+ throw new Error("Percentiles must include at least one value between 0 and 1.");
639
+ }
640
+ return percentiles.map((percentile) => {
641
+ if (!Number.isFinite(percentile) || percentile < 0 || percentile > 1) {
642
+ throw new Error("Percentiles must be finite numbers between 0 and 1.");
643
+ }
644
+ return percentile;
645
+ });
646
+ }
647
+ function buildFeedbackWhereClause(args, includeNumericGuard = false) {
648
+ const conditions = ["feedbackType = ?"];
649
+ const params = [args.feedbackType];
650
+ if (args.feedbackSource !== void 0) {
651
+ conditions.push("feedbackSource = ?");
652
+ params.push(args.feedbackSource);
653
+ }
654
+ const { clause: filterClause, params: filterParams } = buildWhereClause(
655
+ args.filters,
656
+ { source: "feedbackSource" }
657
+ );
658
+ if (filterClause) {
659
+ conditions.push(filterClause.replace("WHERE ", ""));
660
+ params.push(...filterParams);
661
+ }
662
+ if (includeNumericGuard) {
663
+ conditions.push("TRY_CAST(value AS DOUBLE) IS NOT NULL");
664
+ }
665
+ return { clause: `WHERE ${conditions.join(" AND ")}`, params };
666
+ }
667
+ function resolveFeedbackGroupBy(groupBy) {
668
+ return groupBy.map((key, index) => {
669
+ const column = utils.parseFieldKey(key);
670
+ if (!FEEDBACK_GROUP_BY_COLUMNS.has(column)) {
671
+ throw new Error(`Invalid groupBy column(s): ${key}`);
672
+ }
673
+ const alias = `group_by_${index}`;
674
+ return {
675
+ key,
676
+ selectSql: `${column} AS ${alias}`,
677
+ groupSql: alias
678
+ };
679
+ });
680
+ }
681
+ function toSeriesName(values) {
682
+ return values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|");
683
+ }
684
+ function rowToFeedbackRecord(row) {
685
+ const rawValue = row.value;
686
+ let value = rawValue;
687
+ const numValue = Number(rawValue);
688
+ if (!isNaN(numValue)) value = numValue;
689
+ return {
690
+ timestamp: toDate(row.timestamp),
691
+ traceId: row.traceId,
692
+ spanId: row.spanId ?? null,
693
+ experimentId: row.experimentId ?? null,
694
+ entityType: row.entityType ?? null,
695
+ entityId: row.entityId ?? null,
696
+ entityName: row.entityName ?? null,
697
+ parentEntityType: row.parentEntityType ?? null,
698
+ parentEntityId: row.parentEntityId ?? null,
699
+ parentEntityName: row.parentEntityName ?? null,
700
+ rootEntityType: row.rootEntityType ?? null,
701
+ rootEntityId: row.rootEntityId ?? null,
702
+ rootEntityName: row.rootEntityName ?? null,
703
+ userId: row.userId ?? null,
704
+ organizationId: row.organizationId ?? null,
705
+ resourceId: row.resourceId ?? null,
706
+ runId: row.runId ?? null,
707
+ sessionId: row.sessionId ?? null,
708
+ threadId: row.threadId ?? null,
709
+ requestId: row.requestId ?? null,
710
+ environment: row.environment ?? null,
711
+ executionSource: row.executionSource ?? null,
712
+ serviceName: row.serviceName ?? null,
713
+ feedbackUserId: row.feedbackUserId ?? null,
714
+ sourceId: row.sourceId ?? null,
715
+ source: row.feedbackSource,
716
+ feedbackSource: row.feedbackSource,
717
+ feedbackType: row.feedbackType,
718
+ value,
719
+ comment: row.comment ?? null,
720
+ tags: parseJsonArray(row.tags),
721
+ metadata: parseJson(row.metadata),
722
+ scope: parseJson(row.scope)
723
+ };
724
+ }
725
+ function getComparisonDateRange(comparePeriod, timestamp) {
726
+ if (!timestamp.start || !timestamp.end) return null;
727
+ const duration = timestamp.end.getTime() - timestamp.start.getTime();
728
+ switch (comparePeriod) {
729
+ case "previous_period":
730
+ return {
731
+ start: new Date(timestamp.start.getTime() - duration),
732
+ end: new Date(timestamp.end.getTime() - duration),
733
+ startExclusive: timestamp.startExclusive,
734
+ endExclusive: timestamp.endExclusive
735
+ };
736
+ case "previous_day":
737
+ return {
738
+ start: new Date(timestamp.start.getTime() - 864e5),
739
+ end: new Date(timestamp.end.getTime() - 864e5),
740
+ startExclusive: timestamp.startExclusive,
741
+ endExclusive: timestamp.endExclusive
742
+ };
743
+ case "previous_week":
744
+ return {
745
+ start: new Date(timestamp.start.getTime() - 6048e5),
746
+ end: new Date(timestamp.end.getTime() - 6048e5),
747
+ startExclusive: timestamp.startExclusive,
748
+ endExclusive: timestamp.endExclusive
749
+ };
750
+ }
751
+ }
571
752
  async function createFeedback(db, args) {
572
753
  const f = args.feedback;
573
754
  const feedbackSource = f.feedbackSource ?? f.source ?? "";
@@ -681,57 +862,142 @@ async function listFeedback(db, args) {
681
862
  filterParams
682
863
  );
683
864
  const total = Number(countResult[0]?.total ?? 0);
684
- const rows = await db.query(`SELECT * FROM feedback_events ${filterClause} ${orderByClause} ${paginationClause}`, [
685
- ...filterParams,
686
- ...paginationParams
687
- ]);
688
- const feedback = rows.map((row) => {
689
- const r = row;
690
- const rawValue = r.value;
691
- let value = rawValue;
692
- const numValue = Number(rawValue);
693
- if (!isNaN(numValue)) value = numValue;
694
- return {
695
- timestamp: toDate(r.timestamp),
696
- traceId: r.traceId,
697
- spanId: r.spanId ?? null,
698
- experimentId: r.experimentId ?? null,
699
- entityType: r.entityType ?? null,
700
- entityId: r.entityId ?? null,
701
- entityName: r.entityName ?? null,
702
- parentEntityType: r.parentEntityType ?? null,
703
- parentEntityId: r.parentEntityId ?? null,
704
- parentEntityName: r.parentEntityName ?? null,
705
- rootEntityType: r.rootEntityType ?? null,
706
- rootEntityId: r.rootEntityId ?? null,
707
- rootEntityName: r.rootEntityName ?? null,
708
- userId: r.userId ?? null,
709
- organizationId: r.organizationId ?? null,
710
- resourceId: r.resourceId ?? null,
711
- runId: r.runId ?? null,
712
- sessionId: r.sessionId ?? null,
713
- threadId: r.threadId ?? null,
714
- requestId: r.requestId ?? null,
715
- environment: r.environment ?? null,
716
- executionSource: r.executionSource ?? null,
717
- serviceName: r.serviceName ?? null,
718
- feedbackUserId: r.feedbackUserId ?? null,
719
- sourceId: r.sourceId ?? null,
720
- source: r.feedbackSource,
721
- feedbackSource: r.feedbackSource,
722
- feedbackType: r.feedbackType,
723
- value,
724
- comment: r.comment ?? null,
725
- tags: parseJsonArray(r.tags),
726
- metadata: parseJson(r.metadata),
727
- scope: parseJson(r.scope)
728
- };
729
- });
865
+ const rows = await db.query(
866
+ `SELECT * FROM feedback_events ${filterClause} ${orderByClause} ${paginationClause}`,
867
+ [...filterParams, ...paginationParams]
868
+ );
730
869
  return {
731
870
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
732
- feedback
871
+ feedback: rows.map((row) => rowToFeedbackRecord(row))
872
+ };
873
+ }
874
+ async function getFeedbackAggregate(db, args) {
875
+ const aggSql = getAggregationSql(args.aggregation);
876
+ const { clause, params } = buildFeedbackWhereClause(args, true);
877
+ const rows = await db.query(
878
+ `SELECT ${aggSql} AS value FROM feedback_events ${clause}`,
879
+ params
880
+ );
881
+ const value = rows[0]?.value === null || rows[0]?.value === void 0 ? null : Number(rows[0]?.value);
882
+ if (args.comparePeriod && args.filters?.timestamp) {
883
+ const previousTimestamp = getComparisonDateRange(args.comparePeriod, args.filters.timestamp);
884
+ if (previousTimestamp) {
885
+ const previousWhere = buildFeedbackWhereClause(
886
+ {
887
+ ...args,
888
+ filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
889
+ },
890
+ true
891
+ );
892
+ const prevRows = await db.query(
893
+ `SELECT ${aggSql} AS value FROM feedback_events ${previousWhere.clause}`,
894
+ previousWhere.params
895
+ );
896
+ const previousValue = prevRows[0]?.value === null || prevRows[0]?.value === void 0 ? null : Number(prevRows[0]?.value);
897
+ let changePercent = null;
898
+ if (previousValue !== null && previousValue !== 0 && value !== null) {
899
+ changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
900
+ }
901
+ return { value, previousValue, changePercent };
902
+ }
903
+ }
904
+ return { value };
905
+ }
906
+ async function getFeedbackBreakdown(db, args) {
907
+ const aggSql = getAggregationSql(args.aggregation);
908
+ const { clause, params } = buildFeedbackWhereClause(args, true);
909
+ const resolvedGroupBy = resolveFeedbackGroupBy(args.groupBy);
910
+ const sql = `SELECT ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")}, ${aggSql} AS value FROM feedback_events ${clause} GROUP BY ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")} ORDER BY value DESC`;
911
+ const rows = await db.query(sql, params);
912
+ return {
913
+ groups: rows.map((row) => ({
914
+ dimensions: Object.fromEntries(
915
+ resolvedGroupBy.map((entry, index) => {
916
+ const value = row[`group_by_${index}`];
917
+ return [entry.key, value === null || value === void 0 ? null : String(value)];
918
+ })
919
+ ),
920
+ value: Number(row.value ?? 0)
921
+ }))
922
+ };
923
+ }
924
+ async function getFeedbackTimeSeries(db, args) {
925
+ const aggSql = getAggregationSql(args.aggregation);
926
+ const intervalSql = getIntervalSql(args.interval);
927
+ const { clause, params } = buildFeedbackWhereClause(args, true);
928
+ if (args.groupBy && args.groupBy.length > 0) {
929
+ const resolvedGroupBy = resolveFeedbackGroupBy(args.groupBy);
930
+ const sql = `
931
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
932
+ ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")},
933
+ ${aggSql} AS value
934
+ FROM feedback_events ${clause}
935
+ GROUP BY bucket, ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")}
936
+ ORDER BY bucket
937
+ `;
938
+ const rows2 = await db.query(sql, params);
939
+ const seriesMap = /* @__PURE__ */ new Map();
940
+ for (const row of rows2) {
941
+ const groupValues = resolvedGroupBy.map((_, index) => row[`group_by_${index}`]);
942
+ const key = JSON.stringify(groupValues);
943
+ if (!seriesMap.has(key)) {
944
+ seriesMap.set(key, { name: toSeriesName(groupValues), points: [] });
945
+ }
946
+ seriesMap.get(key).points.push({
947
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
948
+ value: Number(row.value ?? 0)
949
+ });
950
+ }
951
+ return { series: Array.from(seriesMap.values()) };
952
+ }
953
+ const rows = await db.query(
954
+ `
955
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
956
+ ${aggSql} AS value
957
+ FROM feedback_events ${clause}
958
+ GROUP BY bucket
959
+ ORDER BY bucket
960
+ `,
961
+ params
962
+ );
963
+ return {
964
+ series: [
965
+ {
966
+ name: args.feedbackSource ? `${args.feedbackType}|${args.feedbackSource}` : args.feedbackType,
967
+ points: rows.map((row) => ({
968
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
969
+ value: Number(row.value ?? 0)
970
+ }))
971
+ }
972
+ ]
733
973
  };
734
974
  }
975
+ async function getFeedbackPercentiles(db, args) {
976
+ const intervalSql = getIntervalSql(args.interval);
977
+ const { clause, params } = buildFeedbackWhereClause(args, true);
978
+ const percentiles = getValidatedPercentiles(args.percentiles);
979
+ const series = [];
980
+ for (const percentile of percentiles) {
981
+ const rows = await db.query(
982
+ `
983
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
984
+ percentile_cont(${percentile}) WITHIN GROUP (ORDER BY TRY_CAST(value AS DOUBLE)) AS pvalue
985
+ FROM feedback_events ${clause}
986
+ GROUP BY bucket
987
+ ORDER BY bucket
988
+ `,
989
+ params
990
+ );
991
+ series.push({
992
+ percentile,
993
+ points: rows.map((row) => ({
994
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
995
+ value: Number(row.pvalue ?? 0)
996
+ }))
997
+ });
998
+ }
999
+ return { series };
1000
+ }
735
1001
 
736
1002
  // src/storage/domains/observability/logs.ts
737
1003
  var COLUMNS = [
@@ -859,7 +1125,7 @@ async function listLogs(db, args) {
859
1125
  logs
860
1126
  };
861
1127
  }
862
- function getAggregationSql(aggregation, measure = "value") {
1128
+ function getAggregationSql2(aggregation, measure = "value") {
863
1129
  switch (aggregation) {
864
1130
  case "sum":
865
1131
  return `SUM(${measure})`;
@@ -877,7 +1143,7 @@ function getAggregationSql(aggregation, measure = "value") {
877
1143
  return `SUM(${measure})`;
878
1144
  }
879
1145
  }
880
- function getIntervalSql(interval) {
1146
+ function getIntervalSql2(interval) {
881
1147
  switch (interval) {
882
1148
  case "1m":
883
1149
  return "1 minute";
@@ -1102,7 +1368,7 @@ async function listMetrics(db, args) {
1102
1368
  };
1103
1369
  }
1104
1370
  async function getMetricAggregate(db, args) {
1105
- const aggSql = getAggregationSql(args.aggregation);
1371
+ const aggSql = getAggregationSql2(args.aggregation);
1106
1372
  const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1107
1373
  const { clause: filterClause, params: filterParams } = buildWhereClause(
1108
1374
  args.filters
@@ -1186,7 +1452,7 @@ async function getMetricAggregate(db, args) {
1186
1452
  return { value, estimatedCost: costSummary.estimatedCost, costUnit: costSummary.costUnit };
1187
1453
  }
1188
1454
  async function getMetricBreakdown(db, args) {
1189
- const aggSql = getAggregationSql(args.aggregation);
1455
+ const aggSql = getAggregationSql2(args.aggregation);
1190
1456
  const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1191
1457
  const { clause: filterClause, params: filterParams } = buildWhereClause(
1192
1458
  args.filters
@@ -1219,8 +1485,8 @@ async function getMetricBreakdown(db, args) {
1219
1485
  return { groups };
1220
1486
  }
1221
1487
  async function getMetricTimeSeries(db, args) {
1222
- const aggSql = getAggregationSql(args.aggregation);
1223
- const intervalSql = getIntervalSql(args.interval);
1488
+ const aggSql = getAggregationSql2(args.aggregation);
1489
+ const intervalSql = getIntervalSql2(args.interval);
1224
1490
  const { clause: nameClause, params: nameParams } = buildMetricNameFilter(args.name);
1225
1491
  const { clause: filterClause, params: filterParams } = buildWhereClause(
1226
1492
  args.filters
@@ -1306,7 +1572,7 @@ async function getMetricTimeSeries(db, args) {
1306
1572
  };
1307
1573
  }
1308
1574
  async function getMetricPercentiles(db, args) {
1309
- const intervalSql = getIntervalSql(args.interval);
1575
+ const intervalSql = getIntervalSql2(args.interval);
1310
1576
  const { clause: filterClause, params: filterParams } = buildWhereClause(
1311
1577
  args.filters
1312
1578
  );
@@ -1376,8 +1642,180 @@ async function getMetricLabelValues(db, args) {
1376
1642
  );
1377
1643
  return { values: rows.map((r) => r.val) };
1378
1644
  }
1379
-
1380
- // src/storage/domains/observability/scores.ts
1645
+ var SCORE_GROUP_BY_COLUMNS = /* @__PURE__ */ new Set([
1646
+ "timestamp",
1647
+ "traceId",
1648
+ "spanId",
1649
+ "experimentId",
1650
+ "scoreTraceId",
1651
+ "entityType",
1652
+ "entityId",
1653
+ "entityName",
1654
+ "parentEntityType",
1655
+ "parentEntityId",
1656
+ "parentEntityName",
1657
+ "rootEntityType",
1658
+ "rootEntityId",
1659
+ "rootEntityName",
1660
+ "userId",
1661
+ "organizationId",
1662
+ "resourceId",
1663
+ "runId",
1664
+ "sessionId",
1665
+ "threadId",
1666
+ "requestId",
1667
+ "environment",
1668
+ "executionSource",
1669
+ "serviceName",
1670
+ "scorerId",
1671
+ "scorerVersion",
1672
+ "scoreSource",
1673
+ "score",
1674
+ "reason"
1675
+ ]);
1676
+ function getAggregationSql3(aggregation, measure = "score") {
1677
+ switch (aggregation) {
1678
+ case "sum":
1679
+ return `SUM(${measure})`;
1680
+ case "avg":
1681
+ return `AVG(${measure})`;
1682
+ case "min":
1683
+ return `MIN(${measure})`;
1684
+ case "max":
1685
+ return `MAX(${measure})`;
1686
+ case "count":
1687
+ return `CAST(COUNT(${measure}) AS DOUBLE)`;
1688
+ case "last":
1689
+ return `arg_max(${measure}, timestamp)`;
1690
+ default:
1691
+ return `SUM(${measure})`;
1692
+ }
1693
+ }
1694
+ function getIntervalSql3(interval) {
1695
+ switch (interval) {
1696
+ case "1m":
1697
+ return "1 minute";
1698
+ case "5m":
1699
+ return "5 minutes";
1700
+ case "15m":
1701
+ return "15 minutes";
1702
+ case "1h":
1703
+ return "1 hour";
1704
+ case "1d":
1705
+ return "1 day";
1706
+ default:
1707
+ return "1 hour";
1708
+ }
1709
+ }
1710
+ function getValidatedPercentiles2(percentiles) {
1711
+ if (!Array.isArray(percentiles) || percentiles.length === 0) {
1712
+ throw new Error("Percentiles must include at least one value between 0 and 1.");
1713
+ }
1714
+ return percentiles.map((percentile) => {
1715
+ if (!Number.isFinite(percentile) || percentile < 0 || percentile > 1) {
1716
+ throw new Error("Percentiles must be finite numbers between 0 and 1.");
1717
+ }
1718
+ return percentile;
1719
+ });
1720
+ }
1721
+ function buildScoreWhereClause(args) {
1722
+ const conditions = ["scorerId = ?"];
1723
+ const params = [args.scorerId];
1724
+ if (args.scoreSource !== void 0) {
1725
+ conditions.push("scoreSource = ?");
1726
+ params.push(args.scoreSource);
1727
+ }
1728
+ const { clause: filterClause, params: filterParams } = buildWhereClause(
1729
+ args.filters,
1730
+ { source: "scoreSource" }
1731
+ );
1732
+ if (filterClause) {
1733
+ conditions.push(filterClause.replace("WHERE ", ""));
1734
+ params.push(...filterParams);
1735
+ }
1736
+ return { clause: `WHERE ${conditions.join(" AND ")}`, params };
1737
+ }
1738
+ function resolveScoreGroupBy(groupBy) {
1739
+ return groupBy.map((key, index) => {
1740
+ const column = utils.parseFieldKey(key);
1741
+ if (!SCORE_GROUP_BY_COLUMNS.has(column)) {
1742
+ throw new Error(`Invalid groupBy column(s): ${key}`);
1743
+ }
1744
+ const alias = `group_by_${index}`;
1745
+ return {
1746
+ key,
1747
+ selectSql: `${column} AS ${alias}`,
1748
+ groupSql: alias
1749
+ };
1750
+ });
1751
+ }
1752
+ function toSeriesName2(values) {
1753
+ return values.map((value) => value === null || value === void 0 ? "" : String(value)).join("|");
1754
+ }
1755
+ function rowToScoreRecord(row) {
1756
+ return {
1757
+ timestamp: toDate(row.timestamp),
1758
+ traceId: row.traceId,
1759
+ spanId: row.spanId ?? null,
1760
+ experimentId: row.experimentId ?? null,
1761
+ scoreTraceId: row.scoreTraceId ?? null,
1762
+ entityType: row.entityType ?? null,
1763
+ entityId: row.entityId ?? null,
1764
+ entityName: row.entityName ?? null,
1765
+ parentEntityType: row.parentEntityType ?? null,
1766
+ parentEntityId: row.parentEntityId ?? null,
1767
+ parentEntityName: row.parentEntityName ?? null,
1768
+ rootEntityType: row.rootEntityType ?? null,
1769
+ rootEntityId: row.rootEntityId ?? null,
1770
+ rootEntityName: row.rootEntityName ?? null,
1771
+ userId: row.userId ?? null,
1772
+ organizationId: row.organizationId ?? null,
1773
+ resourceId: row.resourceId ?? null,
1774
+ runId: row.runId ?? null,
1775
+ sessionId: row.sessionId ?? null,
1776
+ threadId: row.threadId ?? null,
1777
+ requestId: row.requestId ?? null,
1778
+ environment: row.environment ?? null,
1779
+ executionSource: row.executionSource ?? null,
1780
+ serviceName: row.serviceName ?? null,
1781
+ scorerId: row.scorerId,
1782
+ scorerVersion: row.scorerVersion ?? null,
1783
+ source: row.scoreSource ?? null,
1784
+ scoreSource: row.scoreSource ?? null,
1785
+ score: Number(row.score),
1786
+ reason: row.reason ?? null,
1787
+ tags: parseJsonArray(row.tags),
1788
+ metadata: parseJson(row.metadata),
1789
+ scope: parseJson(row.scope)
1790
+ };
1791
+ }
1792
+ function getComparisonDateRange2(comparePeriod, timestamp) {
1793
+ if (!timestamp.start || !timestamp.end) return null;
1794
+ const duration = timestamp.end.getTime() - timestamp.start.getTime();
1795
+ switch (comparePeriod) {
1796
+ case "previous_period":
1797
+ return {
1798
+ start: new Date(timestamp.start.getTime() - duration),
1799
+ end: new Date(timestamp.end.getTime() - duration),
1800
+ startExclusive: timestamp.startExclusive,
1801
+ endExclusive: timestamp.endExclusive
1802
+ };
1803
+ case "previous_day":
1804
+ return {
1805
+ start: new Date(timestamp.start.getTime() - 864e5),
1806
+ end: new Date(timestamp.end.getTime() - 864e5),
1807
+ startExclusive: timestamp.startExclusive,
1808
+ endExclusive: timestamp.endExclusive
1809
+ };
1810
+ case "previous_week":
1811
+ return {
1812
+ start: new Date(timestamp.start.getTime() - 6048e5),
1813
+ end: new Date(timestamp.end.getTime() - 6048e5),
1814
+ startExclusive: timestamp.startExclusive,
1815
+ endExclusive: timestamp.endExclusive
1816
+ };
1817
+ }
1818
+ }
1381
1819
  async function createScore(db, args) {
1382
1820
  const s = args.score;
1383
1821
  const scoreSource = s.scoreSource ?? s.source ?? null;
@@ -1489,53 +1927,138 @@ async function listScores(db, args) {
1489
1927
  filterParams
1490
1928
  );
1491
1929
  const total = Number(countResult[0]?.total ?? 0);
1492
- const rows = await db.query(`SELECT * FROM score_events ${filterClause} ${orderByClause} ${paginationClause}`, [
1493
- ...filterParams,
1494
- ...paginationParams
1495
- ]);
1496
- const scores = rows.map((row) => {
1497
- const r = row;
1498
- return {
1499
- timestamp: toDate(r.timestamp),
1500
- traceId: r.traceId,
1501
- spanId: r.spanId ?? null,
1502
- experimentId: r.experimentId ?? null,
1503
- scoreTraceId: r.scoreTraceId ?? null,
1504
- entityType: r.entityType ?? null,
1505
- entityId: r.entityId ?? null,
1506
- entityName: r.entityName ?? null,
1507
- parentEntityType: r.parentEntityType ?? null,
1508
- parentEntityId: r.parentEntityId ?? null,
1509
- parentEntityName: r.parentEntityName ?? null,
1510
- rootEntityType: r.rootEntityType ?? null,
1511
- rootEntityId: r.rootEntityId ?? null,
1512
- rootEntityName: r.rootEntityName ?? null,
1513
- userId: r.userId ?? null,
1514
- organizationId: r.organizationId ?? null,
1515
- resourceId: r.resourceId ?? null,
1516
- runId: r.runId ?? null,
1517
- sessionId: r.sessionId ?? null,
1518
- threadId: r.threadId ?? null,
1519
- requestId: r.requestId ?? null,
1520
- environment: r.environment ?? null,
1521
- executionSource: r.executionSource ?? null,
1522
- serviceName: r.serviceName ?? null,
1523
- scorerId: r.scorerId,
1524
- scorerVersion: r.scorerVersion ?? null,
1525
- source: r.scoreSource ?? null,
1526
- scoreSource: r.scoreSource ?? null,
1527
- score: Number(r.score),
1528
- reason: r.reason ?? null,
1529
- tags: parseJsonArray(r.tags),
1530
- metadata: parseJson(r.metadata),
1531
- scope: parseJson(r.scope)
1532
- };
1533
- });
1930
+ const rows = await db.query(
1931
+ `SELECT * FROM score_events ${filterClause} ${orderByClause} ${paginationClause}`,
1932
+ [...filterParams, ...paginationParams]
1933
+ );
1534
1934
  return {
1535
1935
  pagination: { total, page, perPage, hasMore: (page + 1) * perPage < total },
1536
- scores
1936
+ scores: rows.map((row) => rowToScoreRecord(row))
1537
1937
  };
1538
1938
  }
1939
+ async function getScoreAggregate(db, args) {
1940
+ const aggSql = getAggregationSql3(args.aggregation);
1941
+ const { clause, params } = buildScoreWhereClause(args);
1942
+ const rows = await db.query(`SELECT ${aggSql} AS value FROM score_events ${clause}`, params);
1943
+ const value = rows[0]?.value === null || rows[0]?.value === void 0 ? null : Number(rows[0]?.value);
1944
+ if (args.comparePeriod && args.filters?.timestamp) {
1945
+ const previousTimestamp = getComparisonDateRange2(args.comparePeriod, args.filters.timestamp);
1946
+ if (previousTimestamp) {
1947
+ const prevRows = await db.query(
1948
+ `SELECT ${aggSql} AS value FROM score_events ${buildScoreWhereClause({
1949
+ ...args,
1950
+ filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
1951
+ }).clause}`,
1952
+ buildScoreWhereClause({
1953
+ ...args,
1954
+ filters: { ...args.filters ?? {}, timestamp: previousTimestamp }
1955
+ }).params
1956
+ );
1957
+ const previousValue = prevRows[0]?.value === null || prevRows[0]?.value === void 0 ? null : Number(prevRows[0]?.value);
1958
+ let changePercent = null;
1959
+ if (previousValue !== null && previousValue !== 0 && value !== null) {
1960
+ changePercent = (value - previousValue) / Math.abs(previousValue) * 100;
1961
+ }
1962
+ return { value, previousValue, changePercent };
1963
+ }
1964
+ }
1965
+ return { value };
1966
+ }
1967
+ async function getScoreBreakdown(db, args) {
1968
+ const aggSql = getAggregationSql3(args.aggregation);
1969
+ const { clause, params } = buildScoreWhereClause(args);
1970
+ const resolvedGroupBy = resolveScoreGroupBy(args.groupBy);
1971
+ const sql = `SELECT ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")}, ${aggSql} AS value FROM score_events ${clause} GROUP BY ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")} ORDER BY value DESC`;
1972
+ const rows = await db.query(sql, params);
1973
+ return {
1974
+ groups: rows.map((row) => ({
1975
+ dimensions: Object.fromEntries(
1976
+ resolvedGroupBy.map((entry, index) => {
1977
+ const value = row[`group_by_${index}`];
1978
+ return [entry.key, value === null || value === void 0 ? null : String(value)];
1979
+ })
1980
+ ),
1981
+ value: Number(row.value ?? 0)
1982
+ }))
1983
+ };
1984
+ }
1985
+ async function getScoreTimeSeries(db, args) {
1986
+ const aggSql = getAggregationSql3(args.aggregation);
1987
+ const intervalSql = getIntervalSql3(args.interval);
1988
+ const { clause, params } = buildScoreWhereClause(args);
1989
+ if (args.groupBy && args.groupBy.length > 0) {
1990
+ const resolvedGroupBy = resolveScoreGroupBy(args.groupBy);
1991
+ const sql = `
1992
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
1993
+ ${resolvedGroupBy.map((entry) => entry.selectSql).join(", ")},
1994
+ ${aggSql} AS value
1995
+ FROM score_events ${clause}
1996
+ GROUP BY bucket, ${resolvedGroupBy.map((entry) => entry.groupSql).join(", ")}
1997
+ ORDER BY bucket
1998
+ `;
1999
+ const rows2 = await db.query(sql, params);
2000
+ const seriesMap = /* @__PURE__ */ new Map();
2001
+ for (const row of rows2) {
2002
+ const groupValues = resolvedGroupBy.map((_, index) => row[`group_by_${index}`]);
2003
+ const key = JSON.stringify(groupValues);
2004
+ if (!seriesMap.has(key)) {
2005
+ seriesMap.set(key, { name: toSeriesName2(groupValues), points: [] });
2006
+ }
2007
+ seriesMap.get(key).points.push({
2008
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2009
+ value: Number(row.value ?? 0)
2010
+ });
2011
+ }
2012
+ return { series: Array.from(seriesMap.values()) };
2013
+ }
2014
+ const rows = await db.query(
2015
+ `
2016
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
2017
+ ${aggSql} AS value
2018
+ FROM score_events ${clause}
2019
+ GROUP BY bucket
2020
+ ORDER BY bucket
2021
+ `,
2022
+ params
2023
+ );
2024
+ return {
2025
+ series: [
2026
+ {
2027
+ name: args.scoreSource ? `${args.scorerId}|${args.scoreSource}` : args.scorerId,
2028
+ points: rows.map((row) => ({
2029
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2030
+ value: Number(row.value ?? 0)
2031
+ }))
2032
+ }
2033
+ ]
2034
+ };
2035
+ }
2036
+ async function getScorePercentiles(db, args) {
2037
+ const intervalSql = getIntervalSql3(args.interval);
2038
+ const { clause, params } = buildScoreWhereClause(args);
2039
+ const percentiles = getValidatedPercentiles2(args.percentiles);
2040
+ const series = [];
2041
+ for (const percentile of percentiles) {
2042
+ const rows = await db.query(
2043
+ `
2044
+ SELECT time_bucket(INTERVAL '${intervalSql}', timestamp) AS bucket,
2045
+ percentile_cont(${percentile}) WITHIN GROUP (ORDER BY score) AS pvalue
2046
+ FROM score_events ${clause}
2047
+ GROUP BY bucket
2048
+ ORDER BY bucket
2049
+ `,
2050
+ params
2051
+ );
2052
+ series.push({
2053
+ percentile,
2054
+ points: rows.map((row) => ({
2055
+ timestamp: row.bucket instanceof Date ? row.bucket : new Date(String(row.bucket)),
2056
+ value: Number(row.pvalue ?? 0)
2057
+ }))
2058
+ });
2059
+ }
2060
+ return { series };
2061
+ }
1539
2062
  var COLUMNS2 = [
1540
2063
  "eventType",
1541
2064
  "timestamp",
@@ -1969,6 +2492,18 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
1969
2492
  async listScores(args) {
1970
2493
  return listScores(this.db, args);
1971
2494
  }
2495
+ async getScoreAggregate(args) {
2496
+ return getScoreAggregate(this.db, args);
2497
+ }
2498
+ async getScoreBreakdown(args) {
2499
+ return getScoreBreakdown(this.db, args);
2500
+ }
2501
+ async getScoreTimeSeries(args) {
2502
+ return getScoreTimeSeries(this.db, args);
2503
+ }
2504
+ async getScorePercentiles(args) {
2505
+ return getScorePercentiles(this.db, args);
2506
+ }
1972
2507
  // Feedback
1973
2508
  async createFeedback(args) {
1974
2509
  return createFeedback(this.db, args);
@@ -1979,8 +2514,20 @@ var ObservabilityStorageDuckDB = class extends storage.ObservabilityStorage {
1979
2514
  async listFeedback(args) {
1980
2515
  return listFeedback(this.db, args);
1981
2516
  }
2517
+ async getFeedbackAggregate(args) {
2518
+ return getFeedbackAggregate(this.db, args);
2519
+ }
2520
+ async getFeedbackBreakdown(args) {
2521
+ return getFeedbackBreakdown(this.db, args);
2522
+ }
2523
+ async getFeedbackTimeSeries(args) {
2524
+ return getFeedbackTimeSeries(this.db, args);
2525
+ }
2526
+ async getFeedbackPercentiles(args) {
2527
+ return getFeedbackPercentiles(this.db, args);
2528
+ }
1982
2529
  };
1983
2530
 
1984
2531
  exports.ObservabilityStorageDuckDB = ObservabilityStorageDuckDB;
1985
- //# sourceMappingURL=observability-AMG7NSKE.cjs.map
1986
- //# sourceMappingURL=observability-AMG7NSKE.cjs.map
2532
+ //# sourceMappingURL=observability-XVQ3ST7G.cjs.map
2533
+ //# sourceMappingURL=observability-XVQ3ST7G.cjs.map