@mastra/playground-ui 21.0.1-alpha.0 → 21.0.1-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.
- package/CHANGELOG.md +29 -0
- package/dist/index.cjs.js +159 -93
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.css +13 -8
- package/dist/index.es.js +159 -93
- package/dist/index.es.js.map +1 -1
- package/dist/src/domains/agents/components/agent-traces-panel.d.ts +9 -2
- package/dist/src/domains/metrics/hooks/use-avg-score-kpi-metrics.d.ts +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @mastra/playground-ui
|
|
2
2
|
|
|
3
|
+
## 21.0.1-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated scores metrics to use aggregate and time series APIs instead of fetching raw scores. Scores Over Time chart now always shows hourly data points regardless of the selected time range. Avg Score KPI uses weighted averages via the aggregate API. Updated metrics card layout to use flexible row heights and refined chart legend styling. ([#14937](https://github.com/mastra-ai/mastra/pull/14937))
|
|
8
|
+
|
|
9
|
+
- Fixed deep links for scorer, observability, and agent trace dialogs. ([#14970](https://github.com/mastra-ai/mastra/pull/14970))
|
|
10
|
+
|
|
11
|
+
Developers can now share and reload URLs that keep the selected trace, scoring tab, span, and score in Studio.
|
|
12
|
+
|
|
13
|
+
**Before**
|
|
14
|
+
`/observability`
|
|
15
|
+
`/agents/chef-agent/traces`
|
|
16
|
+
`/evaluation/scorers/response-quality`
|
|
17
|
+
|
|
18
|
+
**After**
|
|
19
|
+
`/observability?traceId=...&spanId=...&tab=scores&scoreId=...`
|
|
20
|
+
`/agents/chef-agent/traces?traceId=...&spanId=...&tab=scores&scoreId=...`
|
|
21
|
+
`/evaluation/scorers/response-quality?entity=...&scoreId=...`
|
|
22
|
+
|
|
23
|
+
This makes review links reliable across the scorer page, observability, and the agent traces view.
|
|
24
|
+
|
|
25
|
+
- Fixed light theme visibility for metrics charts: CartesianGrid lines now adapt to the current theme, and HorizontalBars use reduced opacity in light mode so labels remain readable. Updated MetricsCard minimum height. ([#14975](https://github.com/mastra-ai/mastra/pull/14975))
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [[`81e4259`](https://github.com/mastra-ai/mastra/commit/81e425939b4ceeb4f586e9b6d89c3b1c1f2d2fe7), [`951b8a1`](https://github.com/mastra-ai/mastra/commit/951b8a1b5ef7e1474c59dc4f2b9fc1a8b1e508b6)]:
|
|
28
|
+
- @mastra/core@1.22.0-alpha.1
|
|
29
|
+
- @mastra/client-js@1.12.1-alpha.1
|
|
30
|
+
- @mastra/react@0.2.22-alpha.1
|
|
31
|
+
|
|
3
32
|
## 21.0.1-alpha.0
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -19774,8 +19774,9 @@ function ScoreDialog({
|
|
|
19774
19774
|
usageContext = "scorerPage"
|
|
19775
19775
|
}) {
|
|
19776
19776
|
const [datasetDialogOpen, setDatasetDialogOpen] = React.useState(false);
|
|
19777
|
-
const { Link } = useLinkComponent();
|
|
19777
|
+
const { Link, paths } = useLinkComponent();
|
|
19778
19778
|
const isCodeBased = isCodeBasedScorer$1(score);
|
|
19779
|
+
const scorerDetailHref = score?.scorerId && score?.entityId ? `${paths.scorerLink(score.scorerId)}?entity=${encodeURIComponent(score.entityId)}&scoreId=${encodeURIComponent(score.id)}` : void 0;
|
|
19779
19780
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
19780
19781
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
19781
19782
|
SideDialog,
|
|
@@ -19838,7 +19839,7 @@ function ScoreDialog({
|
|
|
19838
19839
|
...usageContext === "SpanDialog" ? [
|
|
19839
19840
|
{
|
|
19840
19841
|
label: "Scorer",
|
|
19841
|
-
value: score?.scorer?.name || "-",
|
|
19842
|
+
value: scorerDetailHref ? /* @__PURE__ */ jsxRuntime.jsx(Link, { href: scorerDetailHref, children: score?.scorer?.name || "-" }) : score?.scorer?.name || "-",
|
|
19842
19843
|
key: "scorer-name"
|
|
19843
19844
|
}
|
|
19844
19845
|
] : [],
|
|
@@ -34486,7 +34487,10 @@ function TraceDialog({
|
|
|
34486
34487
|
if (initialSpanId) {
|
|
34487
34488
|
setSelectedSpanId(initialSpanId);
|
|
34488
34489
|
setDialogIsOpen(true);
|
|
34490
|
+
return;
|
|
34489
34491
|
}
|
|
34492
|
+
setSelectedSpanId(void 0);
|
|
34493
|
+
setDialogIsOpen(false);
|
|
34490
34494
|
}, [initialSpanId]);
|
|
34491
34495
|
React.useEffect(() => {
|
|
34492
34496
|
if (spanScoresPage > 0) {
|
|
@@ -34517,11 +34521,17 @@ function TraceDialog({
|
|
|
34517
34521
|
});
|
|
34518
34522
|
const handleSpanClick = (id) => {
|
|
34519
34523
|
if (selectedSpanId === id) {
|
|
34524
|
+
if (traceId) {
|
|
34525
|
+
navigate(computeTraceLink(traceId));
|
|
34526
|
+
}
|
|
34520
34527
|
setSelectedSpanId(void 0);
|
|
34521
|
-
|
|
34522
|
-
|
|
34523
|
-
|
|
34524
|
-
|
|
34528
|
+
return;
|
|
34529
|
+
}
|
|
34530
|
+
setSelectedSpanId(id);
|
|
34531
|
+
setSpanDialogDefaultTab("details");
|
|
34532
|
+
setDialogIsOpen(true);
|
|
34533
|
+
if (traceId) {
|
|
34534
|
+
navigate(computeTraceLink(traceId, id));
|
|
34525
34535
|
}
|
|
34526
34536
|
};
|
|
34527
34537
|
const handleToScoring = () => {
|
|
@@ -39754,11 +39764,31 @@ function BulkAddToDatasetBar({
|
|
|
39754
39764
|
] })
|
|
39755
39765
|
] });
|
|
39756
39766
|
}
|
|
39757
|
-
function AgentTracesPanel({
|
|
39767
|
+
function AgentTracesPanel({
|
|
39768
|
+
agentId,
|
|
39769
|
+
basePath,
|
|
39770
|
+
initialTraceId,
|
|
39771
|
+
initialSpanId,
|
|
39772
|
+
initialSpanTab,
|
|
39773
|
+
initialScoreId
|
|
39774
|
+
}) {
|
|
39758
39775
|
const client = react.useMastraClient();
|
|
39759
39776
|
const filters = useAgentTracesFilters(agentId);
|
|
39760
|
-
const
|
|
39761
|
-
const
|
|
39777
|
+
const { navigate } = useLinkComponent();
|
|
39778
|
+
const buildTraceUrl = React.useCallback(
|
|
39779
|
+
(traceId, spanId, scoreId, tab) => {
|
|
39780
|
+
const params = new URLSearchParams();
|
|
39781
|
+
if (traceId) params.set("traceId", traceId);
|
|
39782
|
+
if (spanId) params.set("spanId", spanId);
|
|
39783
|
+
if (tab) params.set("tab", tab);
|
|
39784
|
+
if (scoreId) params.set("scoreId", scoreId);
|
|
39785
|
+
const query = params.toString();
|
|
39786
|
+
return query ? `${basePath ?? `/agents/${agentId}/traces`}?${query}` : basePath ?? `/agents/${agentId}/traces`;
|
|
39787
|
+
},
|
|
39788
|
+
[agentId, basePath]
|
|
39789
|
+
);
|
|
39790
|
+
const [selectedTraceId, setSelectedTraceId] = React.useState(initialTraceId);
|
|
39791
|
+
const [dialogIsOpen, setDialogIsOpen] = React.useState(Boolean(initialTraceId));
|
|
39762
39792
|
const [checkedTraceIds, setCheckedTraceIds] = React.useState(/* @__PURE__ */ new Set());
|
|
39763
39793
|
const [sort, setSort] = React.useState(null);
|
|
39764
39794
|
const {
|
|
@@ -39871,20 +39901,31 @@ function AgentTracesPanel({ agentId }) {
|
|
|
39871
39901
|
return next.size === prev.size ? prev : next;
|
|
39872
39902
|
});
|
|
39873
39903
|
}, [displayTraces, checkedTraceIds.size]);
|
|
39904
|
+
React.useEffect(() => {
|
|
39905
|
+
if (initialTraceId) {
|
|
39906
|
+
setSelectedTraceId(initialTraceId);
|
|
39907
|
+
setDialogIsOpen(true);
|
|
39908
|
+
return;
|
|
39909
|
+
}
|
|
39910
|
+
setSelectedTraceId(void 0);
|
|
39911
|
+
setDialogIsOpen(false);
|
|
39912
|
+
}, [initialTraceId]);
|
|
39874
39913
|
const { batchInsertItems } = useDatasetMutations();
|
|
39875
39914
|
const allSelected = displayTraces.length > 0 && displayTraces.every((t) => checkedTraceIds.has(t.traceId));
|
|
39876
39915
|
const someSelected = checkedTraceIds.size > 0;
|
|
39877
39916
|
const handleTraceClick = React.useCallback(
|
|
39878
39917
|
(traceId) => {
|
|
39879
39918
|
if (selectedTraceId === traceId) {
|
|
39919
|
+
navigate(buildTraceUrl());
|
|
39880
39920
|
setSelectedTraceId(void 0);
|
|
39881
39921
|
setDialogIsOpen(false);
|
|
39882
39922
|
} else {
|
|
39923
|
+
navigate(buildTraceUrl(traceId));
|
|
39883
39924
|
setSelectedTraceId(traceId);
|
|
39884
39925
|
setDialogIsOpen(true);
|
|
39885
39926
|
}
|
|
39886
39927
|
},
|
|
39887
|
-
[selectedTraceId]
|
|
39928
|
+
[buildTraceUrl, navigate, selectedTraceId]
|
|
39888
39929
|
);
|
|
39889
39930
|
const handleCheckToggle = React.useCallback((traceId, checked) => {
|
|
39890
39931
|
setCheckedTraceIds((prev) => {
|
|
@@ -39923,14 +39964,16 @@ function AgentTracesPanel({ agentId }) {
|
|
|
39923
39964
|
},
|
|
39924
39965
|
[displayTraces, checkedTraceIds, batchInsertItems]
|
|
39925
39966
|
);
|
|
39926
|
-
const computeTraceLink = React.useCallback(
|
|
39927
|
-
|
|
39928
|
-
|
|
39967
|
+
const computeTraceLink = React.useCallback(
|
|
39968
|
+
(traceId, spanId, tab) => buildTraceUrl(traceId, spanId, void 0, tab),
|
|
39969
|
+
[buildTraceUrl]
|
|
39970
|
+
);
|
|
39929
39971
|
const toNextTrace = React.useMemo(
|
|
39930
39972
|
() => getToNextEntryFn({
|
|
39931
39973
|
entries: displayTraces.map((t) => ({ id: t.traceId })),
|
|
39932
39974
|
id: selectedTraceId,
|
|
39933
39975
|
update: (id) => {
|
|
39976
|
+
navigate(buildTraceUrl(id));
|
|
39934
39977
|
setSelectedTraceId(id);
|
|
39935
39978
|
setDialogIsOpen(true);
|
|
39936
39979
|
}
|
|
@@ -39942,6 +39985,7 @@ function AgentTracesPanel({ agentId }) {
|
|
|
39942
39985
|
entries: displayTraces.map((t) => ({ id: t.traceId })),
|
|
39943
39986
|
id: selectedTraceId,
|
|
39944
39987
|
update: (id) => {
|
|
39988
|
+
navigate(buildTraceUrl(id));
|
|
39945
39989
|
setSelectedTraceId(id);
|
|
39946
39990
|
setDialogIsOpen(true);
|
|
39947
39991
|
}
|
|
@@ -40043,6 +40087,7 @@ function AgentTracesPanel({ agentId }) {
|
|
|
40043
40087
|
traceDetails: selectedTrace?.spans?.find((s) => s.traceId === selectedTraceId && !s.parentSpanId),
|
|
40044
40088
|
isOpen: dialogIsOpen,
|
|
40045
40089
|
onClose: () => {
|
|
40090
|
+
navigate(buildTraceUrl());
|
|
40046
40091
|
setDialogIsOpen(false);
|
|
40047
40092
|
setSelectedTraceId(void 0);
|
|
40048
40093
|
},
|
|
@@ -40050,6 +40095,9 @@ function AgentTracesPanel({ agentId }) {
|
|
|
40050
40095
|
onPrevious: toPreviousTrace,
|
|
40051
40096
|
isLoadingSpans: isSelectedTraceLoading,
|
|
40052
40097
|
computeTraceLink,
|
|
40098
|
+
initialSpanId,
|
|
40099
|
+
initialSpanTab,
|
|
40100
|
+
initialScoreId,
|
|
40053
40101
|
scorers: scorersMap,
|
|
40054
40102
|
isLoadingScorers
|
|
40055
40103
|
}
|
|
@@ -41906,7 +41954,7 @@ function MetricsCardRoot({ children, className }) {
|
|
|
41906
41954
|
DashboardCard,
|
|
41907
41955
|
{
|
|
41908
41956
|
className: cn(
|
|
41909
|
-
"flex-1 grid grid-rows-[
|
|
41957
|
+
"flex-1 grid grid-rows-[4rem_1fr] min-h-72 gap-2 min-w-80 md:min-w-[22rem] lg:min-w-[24rem] xl:min-w-[26rem] 2xl:min-w-[30rem]",
|
|
41910
41958
|
className
|
|
41911
41959
|
),
|
|
41912
41960
|
children
|
|
@@ -42057,12 +42105,12 @@ function MetricsLineChart({
|
|
|
42057
42105
|
yDomain
|
|
42058
42106
|
}) {
|
|
42059
42107
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
42060
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap w-full items-end gap-4 gap-y-1 mb-4", children: series.map((s) => {
|
|
42108
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap w-full items-end gap-4 gap-y-1 mb-4 ", children: series.map((s) => {
|
|
42061
42109
|
const aggregated = s.aggregate?.(data);
|
|
42062
42110
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-baseline gap-2", children: [
|
|
42063
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-2 shrink-0 rounded-full translate-y-
|
|
42111
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-2 shrink-0 rounded-full -translate-y-px", style: { backgroundColor: s.color } }),
|
|
42064
42112
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui-sm text-neutral3 truncate max-w-24", children: s.label }),
|
|
42065
|
-
aggregated && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-ui-
|
|
42113
|
+
aggregated && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-ui-sm text-neutral4", children: [
|
|
42066
42114
|
aggregated.value,
|
|
42067
42115
|
aggregated.suffix && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-ui-sm text-neutral2", children: [
|
|
42068
42116
|
" ",
|
|
@@ -42072,7 +42120,15 @@ function MetricsLineChart({
|
|
|
42072
42120
|
] }, s.dataKey);
|
|
42073
42121
|
}) }),
|
|
42074
42122
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { height }, children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, children: [
|
|
42075
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42123
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42124
|
+
recharts.CartesianGrid,
|
|
42125
|
+
{
|
|
42126
|
+
stroke: "currentColor",
|
|
42127
|
+
strokeOpacity: 0.08,
|
|
42128
|
+
vertical: false,
|
|
42129
|
+
className: "text-black dark:text-white"
|
|
42130
|
+
}
|
|
42131
|
+
),
|
|
42076
42132
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42077
42133
|
recharts.XAxis,
|
|
42078
42134
|
{
|
|
@@ -42183,24 +42239,25 @@ function useAvgScoreKpiMetrics() {
|
|
|
42183
42239
|
if (scorerIds.length === 0) {
|
|
42184
42240
|
return { value: null, previousValue: null, changePercent: null };
|
|
42185
42241
|
}
|
|
42186
|
-
const
|
|
42187
|
-
|
|
42242
|
+
const filters = {
|
|
42243
|
+
timestamp: { start: timestamp.start, end: timestamp.end }
|
|
42244
|
+
};
|
|
42245
|
+
const results = await Promise.all(
|
|
42246
|
+
scorerIds.map(async (scorerId) => {
|
|
42247
|
+
const [avg2, count] = await Promise.all([
|
|
42248
|
+
client.getScoreAggregate({ scorerId, aggregation: "avg", filters }),
|
|
42249
|
+
client.getScoreAggregate({ scorerId, aggregation: "count", filters })
|
|
42250
|
+
]);
|
|
42251
|
+
return { avg: avg2.value ?? 0, count: count.value ?? 0 };
|
|
42252
|
+
})
|
|
42188
42253
|
);
|
|
42189
|
-
const
|
|
42190
|
-
|
|
42191
|
-
const allScoreValues = [];
|
|
42192
|
-
for (const result of allResults) {
|
|
42193
|
-
for (const s of result?.scores ?? []) {
|
|
42194
|
-
const ts = new Date(s.createdAt).getTime();
|
|
42195
|
-
if (ts >= startMs && ts <= endMs) {
|
|
42196
|
-
allScoreValues.push(s.score);
|
|
42197
|
-
}
|
|
42198
|
-
}
|
|
42199
|
-
}
|
|
42200
|
-
if (allScoreValues.length === 0) {
|
|
42254
|
+
const withData = results.filter((r) => r.count > 0);
|
|
42255
|
+
if (withData.length === 0) {
|
|
42201
42256
|
return { value: null, previousValue: null, changePercent: null };
|
|
42202
42257
|
}
|
|
42203
|
-
const
|
|
42258
|
+
const totalCount = withData.reduce((sum, r) => sum + r.count, 0);
|
|
42259
|
+
const weightedSum = withData.reduce((sum, r) => sum + r.avg * r.count, 0);
|
|
42260
|
+
const avg = weightedSum / totalCount;
|
|
42204
42261
|
return { value: Math.round(avg * 100) / 100, previousValue: null, changePercent: null };
|
|
42205
42262
|
}
|
|
42206
42263
|
});
|
|
@@ -42479,78 +42536,84 @@ function useScoresMetrics() {
|
|
|
42479
42536
|
return reactQuery.useQuery({
|
|
42480
42537
|
queryKey: ["metrics", "scores-card", datePreset, customRange],
|
|
42481
42538
|
queryFn: async () => {
|
|
42539
|
+
const filters = {
|
|
42540
|
+
timestamp: { start: timestamp.start, end: timestamp.end }
|
|
42541
|
+
};
|
|
42482
42542
|
const scorersMap = await client.listScorers();
|
|
42483
42543
|
const scorerIds = Object.keys(scorersMap ?? {});
|
|
42484
42544
|
if (scorerIds.length === 0) {
|
|
42485
42545
|
return { summaryData: [], overTimeData: [], scorerNames: [], avgScore: null };
|
|
42486
42546
|
}
|
|
42487
|
-
const
|
|
42488
|
-
|
|
42489
|
-
|
|
42547
|
+
const summaryResults = await Promise.all(
|
|
42548
|
+
scorerIds.map(async (scorerId) => {
|
|
42549
|
+
const [avg, min, max, count] = await Promise.all([
|
|
42550
|
+
client.getScoreAggregate({ scorerId, aggregation: "avg", filters }),
|
|
42551
|
+
client.getScoreAggregate({ scorerId, aggregation: "min", filters }),
|
|
42552
|
+
client.getScoreAggregate({ scorerId, aggregation: "max", filters }),
|
|
42553
|
+
client.getScoreAggregate({ scorerId, aggregation: "count", filters })
|
|
42554
|
+
]);
|
|
42555
|
+
return {
|
|
42556
|
+
scorer: scorerId,
|
|
42557
|
+
avg: avg.value ?? 0,
|
|
42558
|
+
min: min.value ?? 0,
|
|
42559
|
+
max: max.value ?? 0,
|
|
42560
|
+
count: count.value ?? 0
|
|
42561
|
+
};
|
|
42562
|
+
})
|
|
42490
42563
|
);
|
|
42491
|
-
const
|
|
42492
|
-
const endMs = timestamp.end.getTime();
|
|
42493
|
-
const allScores = [];
|
|
42494
|
-
for (let i = 0; i < scorerIds.length; i++) {
|
|
42495
|
-
const scores = allResults[i]?.scores ?? [];
|
|
42496
|
-
for (const s of scores) {
|
|
42497
|
-
const ts = new Date(s.createdAt).getTime();
|
|
42498
|
-
if (ts >= startMs && ts <= endMs) {
|
|
42499
|
-
allScores.push({
|
|
42500
|
-
scorerId: scorerIds[i],
|
|
42501
|
-
score: s.score,
|
|
42502
|
-
createdAt: s.createdAt
|
|
42503
|
-
});
|
|
42504
|
-
}
|
|
42505
|
-
}
|
|
42506
|
-
}
|
|
42507
|
-
if (allScores.length === 0) {
|
|
42508
|
-
return { summaryData: [], overTimeData: [], scorerNames: [], avgScore: null };
|
|
42509
|
-
}
|
|
42510
|
-
const byScorer = /* @__PURE__ */ new Map();
|
|
42511
|
-
for (const s of allScores) {
|
|
42512
|
-
if (!byScorer.has(s.scorerId)) byScorer.set(s.scorerId, []);
|
|
42513
|
-
byScorer.get(s.scorerId).push(s.score);
|
|
42514
|
-
}
|
|
42515
|
-
const summaryData = Array.from(byScorer.entries()).map(([scorer, vals]) => ({
|
|
42516
|
-
scorer,
|
|
42517
|
-
avg: vals.reduce((a, b) => a + b, 0) / vals.length,
|
|
42518
|
-
min: Math.min(...vals),
|
|
42519
|
-
max: Math.max(...vals),
|
|
42520
|
-
count: vals.length
|
|
42521
|
-
}));
|
|
42564
|
+
const summaryData = summaryResults.filter((s) => s.count > 0);
|
|
42522
42565
|
const scorerNames = summaryData.map((s) => s.scorer);
|
|
42523
|
-
|
|
42524
|
-
|
|
42525
|
-
for (const s of allScores) {
|
|
42526
|
-
const ts = new Date(s.createdAt);
|
|
42527
|
-
const bucket = Math.floor(ts.getTime() / 36e5) * 36e5;
|
|
42528
|
-
if (!bucketMap.has(bucket)) bucketMap.set(bucket, /* @__PURE__ */ new Map());
|
|
42529
|
-
const scorerMap = bucketMap.get(bucket);
|
|
42530
|
-
if (!scorerMap.has(s.scorerId)) scorerMap.set(s.scorerId, []);
|
|
42531
|
-
scorerMap.get(s.scorerId).push(s.score);
|
|
42566
|
+
if (summaryData.length === 0) {
|
|
42567
|
+
return { summaryData: [], overTimeData: [], scorerNames: [], avgScore: null };
|
|
42532
42568
|
}
|
|
42533
|
-
const
|
|
42534
|
-
|
|
42535
|
-
|
|
42536
|
-
|
|
42537
|
-
|
|
42538
|
-
|
|
42569
|
+
const totalWeighted = summaryData.reduce((s, d) => s + d.avg * d.count, 0);
|
|
42570
|
+
const totalCount = summaryData.reduce((s, d) => s + d.count, 0);
|
|
42571
|
+
const avgScore = totalCount ? Math.round(totalWeighted / totalCount * 100) / 100 : 0;
|
|
42572
|
+
const interval = "1h";
|
|
42573
|
+
const timeSeriesResults = await Promise.all(
|
|
42574
|
+
scorerNames.map(
|
|
42575
|
+
(scorerId) => client.getScoreTimeSeries({
|
|
42576
|
+
scorerId,
|
|
42577
|
+
interval,
|
|
42578
|
+
aggregation: "avg",
|
|
42579
|
+
filters
|
|
42539
42580
|
})
|
|
42540
|
-
|
|
42541
|
-
|
|
42542
|
-
|
|
42543
|
-
|
|
42544
|
-
|
|
42581
|
+
)
|
|
42582
|
+
);
|
|
42583
|
+
const bucketMap = /* @__PURE__ */ new Map();
|
|
42584
|
+
const rangeSpansDays = timestamp.end.toDateString() !== timestamp.start.toDateString();
|
|
42585
|
+
for (let i = 0; i < scorerNames.length; i++) {
|
|
42586
|
+
const scorerId = scorerNames[i];
|
|
42587
|
+
const series = timeSeriesResults[i]?.series ?? [];
|
|
42588
|
+
for (const s of series) {
|
|
42589
|
+
for (const point of s.points) {
|
|
42590
|
+
const ts = new Date(point.timestamp);
|
|
42591
|
+
const key = ts.toISOString();
|
|
42592
|
+
if (!bucketMap.has(key)) {
|
|
42593
|
+
bucketMap.set(key, {
|
|
42594
|
+
time: rangeSpansDays ? ts.toLocaleString("en-US", {
|
|
42595
|
+
month: "short",
|
|
42596
|
+
day: "numeric",
|
|
42597
|
+
hour: "2-digit",
|
|
42598
|
+
minute: "2-digit",
|
|
42599
|
+
hour12: false
|
|
42600
|
+
}) : ts.toLocaleTimeString("en-US", {
|
|
42601
|
+
hour: "2-digit",
|
|
42602
|
+
minute: "2-digit",
|
|
42603
|
+
hour12: false
|
|
42604
|
+
})
|
|
42605
|
+
});
|
|
42606
|
+
}
|
|
42607
|
+
bucketMap.get(key)[scorerId] = +point.value.toFixed(2);
|
|
42545
42608
|
}
|
|
42546
42609
|
}
|
|
42547
|
-
|
|
42548
|
-
|
|
42610
|
+
}
|
|
42611
|
+
const overTimeData = Array.from(bucketMap.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([, point]) => point);
|
|
42549
42612
|
return {
|
|
42550
42613
|
summaryData,
|
|
42551
42614
|
overTimeData,
|
|
42552
42615
|
scorerNames,
|
|
42553
|
-
avgScore
|
|
42616
|
+
avgScore
|
|
42554
42617
|
};
|
|
42555
42618
|
}
|
|
42556
42619
|
});
|
|
@@ -42595,7 +42658,7 @@ function ScoresCard() {
|
|
|
42595
42658
|
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "over-time", children: "Over Time" }),
|
|
42596
42659
|
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "summary", children: "Summary" })
|
|
42597
42660
|
] }),
|
|
42598
|
-
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "over-time", children: data.overTimeData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(MetricsLineChart, { data: data.overTimeData, series, yDomain: [0, 1] }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No time series data yet" }) }),
|
|
42661
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "over-time", className: "pb-0", children: data.overTimeData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(MetricsLineChart, { data: data.overTimeData, series, yDomain: [0, 1] }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No time series data yet" }) }),
|
|
42599
42662
|
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "summary", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42600
42663
|
MetricsDataTable,
|
|
42601
42664
|
{
|
|
@@ -42733,7 +42796,7 @@ function HorizontalBars({
|
|
|
42733
42796
|
"div",
|
|
42734
42797
|
{
|
|
42735
42798
|
className: cn(
|
|
42736
|
-
"absolute inset-y-0",
|
|
42799
|
+
"absolute inset-y-0 opacity-40 dark:opacity-100",
|
|
42737
42800
|
si === 0 && "rounded-l",
|
|
42738
42801
|
isLastWithValue && "rounded-r"
|
|
42739
42802
|
),
|
|
@@ -42749,7 +42812,7 @@ function HorizontalBars({
|
|
|
42749
42812
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42750
42813
|
"div",
|
|
42751
42814
|
{
|
|
42752
|
-
className: "absolute inset-y-0 left-0 rounded",
|
|
42815
|
+
className: "absolute inset-y-0 left-0 rounded opacity-40 dark:opacity-100",
|
|
42753
42816
|
style: { width: `${pct}%`, backgroundColor: seg.color }
|
|
42754
42817
|
},
|
|
42755
42818
|
seg.label
|
|
@@ -43845,6 +43908,9 @@ function SpanScoreList({
|
|
|
43845
43908
|
const score = scoresData?.scores?.find((s) => s?.id === scoreId);
|
|
43846
43909
|
setSelectedScore(score);
|
|
43847
43910
|
setDialogIsOpen(true);
|
|
43911
|
+
if (traceId) {
|
|
43912
|
+
navigate(`${computeTraceLink(traceId, spanId)}&tab=scores&scoreId=${encodeURIComponent(scoreId)}`);
|
|
43913
|
+
}
|
|
43848
43914
|
};
|
|
43849
43915
|
if (isLoadingScoresData) {
|
|
43850
43916
|
return /* @__PURE__ */ jsxRuntime.jsx(EntryListSkeleton, { columns: traceScoresListColumns });
|