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