@mastra/playground-ui 23.0.2-alpha.2 → 24.0.0-alpha.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.
- package/CHANGELOG.md +28 -0
- package/dist/index.cjs.js +4254 -13
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.css +354 -84
- package/dist/index.es.js +4137 -16
- package/dist/index.es.js.map +1 -1
- package/dist/src/domains/logs/components/index.d.ts +6 -0
- package/dist/src/domains/logs/components/log-details-view.d.ts +12 -0
- package/dist/src/domains/logs/components/logs-error-content.d.ts +14 -0
- package/dist/src/domains/logs/components/logs-layout.d.ts +19 -0
- package/dist/src/domains/logs/components/logs-list-view.d.ts +20 -0
- package/dist/src/domains/logs/components/logs-toolbar.d.ts +13 -0
- package/dist/src/domains/logs/components/no-logs-info.d.ts +1 -0
- package/dist/src/domains/logs/hooks/index.d.ts +4 -0
- package/dist/src/domains/logs/hooks/use-logs-filter-persistence.d.ts +22 -0
- package/dist/src/domains/logs/hooks/use-logs-list-navigation.d.ts +22 -0
- package/dist/src/domains/logs/hooks/use-logs-url-state.d.ts +38 -0
- package/dist/src/domains/logs/hooks/use-logs.d.ts +1271 -0
- package/dist/src/domains/logs/index.d.ts +4 -0
- package/dist/src/domains/logs/log-filters.d.ts +109 -0
- package/dist/src/domains/logs/types.d.ts +35 -0
- package/dist/src/domains/metrics/components/bar-list.d.ts +21 -0
- package/dist/src/domains/metrics/components/date-range-selector.d.ts +1 -0
- package/dist/src/domains/metrics/components/index.d.ts +9 -0
- package/dist/src/domains/metrics/components/kpi-card-view.d.ts +9 -0
- package/dist/src/domains/metrics/components/latency-card-view.d.ts +11 -0
- package/dist/src/domains/metrics/components/metrics-utils.d.ts +15 -0
- package/dist/src/domains/metrics/components/model-usage-cost-card-view.d.ts +7 -0
- package/dist/src/domains/metrics/components/scores-card-view.d.ts +12 -0
- package/dist/src/domains/metrics/components/token-usage-by-agent-card-view.d.ts +7 -0
- package/dist/src/domains/metrics/components/traces-volume-card-view.d.ts +11 -0
- package/dist/src/domains/metrics/hooks/index.d.ts +12 -0
- package/dist/src/domains/metrics/hooks/use-agent-runs-kpi-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-avg-score-kpi-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-latency-metrics.d.ts +11 -0
- package/dist/src/domains/metrics/hooks/use-metrics-filters.d.ts +9 -0
- package/dist/src/domains/metrics/hooks/use-metrics.d.ts +43 -0
- package/dist/src/domains/metrics/hooks/use-model-cost-kpi-metrics.d.ts +7 -0
- package/dist/src/domains/metrics/hooks/use-model-usage-cost-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/hooks/use-scores-metrics.d.ts +22 -0
- package/dist/src/domains/metrics/hooks/use-token-usage-by-agent-metrics.d.ts +9 -0
- package/dist/src/domains/metrics/hooks/use-total-tokens-kpi-metrics.d.ts +6 -0
- package/dist/src/domains/metrics/hooks/use-trace-volume-metrics.d.ts +10 -0
- package/dist/src/domains/metrics/index.d.ts +2 -0
- package/dist/src/domains/traces/components/format-hierarchical-spans.d.ts +12 -0
- package/dist/src/domains/traces/components/index.d.ts +18 -0
- package/dist/src/domains/traces/components/shared.d.ts +3 -0
- package/dist/src/domains/traces/components/span-data-panel-view.d.ts +26 -0
- package/dist/src/domains/traces/components/span-details-view.d.ts +14 -0
- package/dist/src/domains/traces/components/span-token-usage.d.ts +22 -0
- package/dist/src/domains/traces/components/timeline-expand-col.d.ts +12 -0
- package/dist/src/domains/traces/components/timeline-name-col.d.ts +15 -0
- package/dist/src/domains/traces/components/timeline-structure-sign.d.ts +5 -0
- package/dist/src/domains/traces/components/timeline-timing-col.d.ts +12 -0
- package/dist/src/domains/traces/components/trace-data-panel-view.d.ts +28 -0
- package/dist/src/domains/traces/components/trace-details-view.d.ts +18 -0
- package/dist/src/domains/traces/components/trace-keys-and-values.d.ts +16 -0
- package/dist/src/domains/traces/components/trace-timeline-span.d.ts +18 -0
- package/dist/src/domains/traces/components/trace-timeline.d.ts +15 -0
- package/dist/src/domains/traces/components/traces-error-content.d.ts +16 -0
- package/dist/src/domains/traces/components/traces-layout.d.ts +18 -0
- package/dist/src/domains/traces/components/traces-list-view.d.ts +39 -0
- package/dist/src/domains/traces/components/traces-toolbar.d.ts +19 -0
- package/dist/src/domains/traces/hooks/get-all-span-ids.d.ts +3 -0
- package/dist/src/domains/traces/hooks/index.d.ts +13 -0
- package/dist/src/domains/traces/hooks/use-entity-names.d.ts +7 -0
- package/dist/src/domains/traces/hooks/use-environments.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-service-names.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-span-detail.d.ts +46 -0
- package/dist/src/domains/traces/hooks/use-tags.d.ts +1 -0
- package/dist/src/domains/traces/hooks/use-trace-filter-persistence.d.ts +31 -0
- package/dist/src/domains/traces/hooks/use-trace-light-spans.d.ts +19 -0
- package/dist/src/domains/traces/hooks/use-trace-list-navigation.d.ts +12 -0
- package/dist/src/domains/traces/hooks/use-trace-span-navigation.d.ts +10 -0
- package/dist/src/domains/traces/hooks/use-trace-spans.d.ts +47 -0
- package/dist/src/domains/traces/hooks/use-trace-url-state.d.ts +53 -0
- package/dist/src/domains/traces/hooks/use-traces.d.ts +1549 -0
- package/dist/src/domains/traces/index.d.ts +8 -0
- package/dist/src/domains/traces/trace-filters.d.ts +121 -0
- package/dist/src/domains/traces/types.d.ts +35 -0
- package/dist/src/domains/traces/utils/group-traces-by-thread.d.ts +20 -0
- package/dist/src/domains/traces/utils/index.d.ts +2 -0
- package/dist/src/domains/traces/utils/span-utils.d.ts +15 -0
- package/dist/src/index.d.ts +3 -0
- package/package.json +13 -10
package/dist/index.cjs.js
CHANGED
|
@@ -50,6 +50,8 @@ const isToday = require('date-fns/isToday');
|
|
|
50
50
|
const search = require('@codemirror/search');
|
|
51
51
|
const recharts = require('recharts');
|
|
52
52
|
const reactResizablePanels = require('react-resizable-panels');
|
|
53
|
+
const reactQuery = require('@tanstack/react-query');
|
|
54
|
+
const observability = require('@mastra/core/observability');
|
|
53
55
|
|
|
54
56
|
function _interopNamespaceDefault(e) {
|
|
55
57
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
|
|
@@ -12062,16 +12064,16 @@ function DataListSkeleton({ columns = "auto 1fr auto auto", numberOfRows = 3 })
|
|
|
12062
12064
|
)) });
|
|
12063
12065
|
}
|
|
12064
12066
|
|
|
12065
|
-
function toDate$
|
|
12067
|
+
function toDate$3(value) {
|
|
12066
12068
|
const date = value instanceof Date ? value : new Date(value);
|
|
12067
12069
|
return isNaN(date.getTime()) ? null : date;
|
|
12068
12070
|
}
|
|
12069
12071
|
function ScoresDataListDateCell({ timestamp }) {
|
|
12070
|
-
const date = toDate$
|
|
12072
|
+
const date = toDate$3(timestamp);
|
|
12071
12073
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd text-neutral2", children: date ? dateFns.isToday(date) ? "Today" : dateFns.format(date, "MMM dd") : "-" });
|
|
12072
12074
|
}
|
|
12073
12075
|
function ScoresDataListTimeCell({ timestamp }) {
|
|
12074
|
-
const date = toDate$
|
|
12076
|
+
const date = toDate$3(timestamp);
|
|
12075
12077
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd text-neutral3", children: date ? dateFns.format(date, "h:mm:ss aaa") : "-" });
|
|
12076
12078
|
}
|
|
12077
12079
|
function ScoresDataListInputCell({ input }) {
|
|
@@ -12101,7 +12103,7 @@ const ScoresDataList = Object.assign(ScoresDataListRoot, {
|
|
|
12101
12103
|
ScoreCell: ScoresDataListScoreCell
|
|
12102
12104
|
});
|
|
12103
12105
|
|
|
12104
|
-
function toDate$
|
|
12106
|
+
function toDate$2(value) {
|
|
12105
12107
|
const date = value instanceof Date ? value : new Date(value);
|
|
12106
12108
|
return isNaN(date.getTime()) ? null : date;
|
|
12107
12109
|
}
|
|
@@ -12113,11 +12115,11 @@ function TracesDataListIdCell({ traceId }) {
|
|
|
12113
12115
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd font-mono text-neutral3", children: getShortId(traceId) });
|
|
12114
12116
|
}
|
|
12115
12117
|
function TracesDataListDateCell({ timestamp }) {
|
|
12116
|
-
const date = toDate$
|
|
12118
|
+
const date = toDate$2(timestamp);
|
|
12117
12119
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd text-neutral2", children: date ? dateFns.isToday(date) ? "Today" : dateFns.format(date, "MMM dd") : "-" });
|
|
12118
12120
|
}
|
|
12119
12121
|
function TracesDataListTimeCell({ timestamp }) {
|
|
12120
|
-
const date = toDate$
|
|
12122
|
+
const date = toDate$2(timestamp);
|
|
12121
12123
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd font-mono text-neutral3 flex", children: date ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12122
12124
|
dateFns.format(date, "HH:mm:ss"),
|
|
12123
12125
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-neutral2", children: [
|
|
@@ -12398,7 +12400,7 @@ function EntityListSkeleton({ columns, numberOfRows = 3 }) {
|
|
|
12398
12400
|
)) });
|
|
12399
12401
|
}
|
|
12400
12402
|
|
|
12401
|
-
function toDate(value) {
|
|
12403
|
+
function toDate$1(value) {
|
|
12402
12404
|
const date = value instanceof Date ? value : new Date(value);
|
|
12403
12405
|
return isNaN(date.getTime()) ? null : date;
|
|
12404
12406
|
}
|
|
@@ -12414,11 +12416,11 @@ function LogsDataListLevelCell({ level }) {
|
|
|
12414
12416
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase text-ui-sm font-semibold", style: { color: config.color }, children: config.label }) });
|
|
12415
12417
|
}
|
|
12416
12418
|
function LogsDataListDateCell({ timestamp }) {
|
|
12417
|
-
const date = toDate(timestamp);
|
|
12419
|
+
const date = toDate$1(timestamp);
|
|
12418
12420
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd text-neutral2", children: date ? dateFns.isToday(date) ? "Today" : dateFns.format(date, "MMM dd") : "-" });
|
|
12419
12421
|
}
|
|
12420
12422
|
function LogsDataListTimeCell({ timestamp }) {
|
|
12421
|
-
const date = toDate(timestamp);
|
|
12423
|
+
const date = toDate$1(timestamp);
|
|
12422
12424
|
return /* @__PURE__ */ jsxRuntime.jsx(DataListCell, { height: "compact", className: "text-ui-smd font-mono text-neutral3 flex", children: date ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12423
12425
|
dateFns.format(date, "HH:mm:ss"),
|
|
12424
12426
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-neutral2", children: [
|
|
@@ -13543,7 +13545,7 @@ const DataPanel = Object.assign(DataPanelRoot, {
|
|
|
13543
13545
|
CodeSection: DataCodeSection
|
|
13544
13546
|
});
|
|
13545
13547
|
|
|
13546
|
-
const DATE_PRESETS = [
|
|
13548
|
+
const DATE_PRESETS$1 = [
|
|
13547
13549
|
{ value: "all", label: "All" },
|
|
13548
13550
|
{ value: "last-24h", label: "Last 24 hours", ms: 24 * 60 * 60 * 1e3 },
|
|
13549
13551
|
{ value: "last-3d", label: "Last 3 days", ms: 3 * 24 * 60 * 60 * 1e3 },
|
|
@@ -13567,7 +13569,7 @@ function DateTimeRangePicker({
|
|
|
13567
13569
|
presets,
|
|
13568
13570
|
size = "md"
|
|
13569
13571
|
}) {
|
|
13570
|
-
const visiblePresets = presets ? DATE_PRESETS.filter((p) => presets.includes(p.value)) : DATE_PRESETS;
|
|
13572
|
+
const visiblePresets = presets ? DATE_PRESETS$1.filter((p) => presets.includes(p.value)) : DATE_PRESETS$1;
|
|
13571
13573
|
const fallbackPreset = visiblePresets.find((p) => p.value !== "custom")?.value ?? "all";
|
|
13572
13574
|
const [customRangeOpen, setCustomRangeOpen] = React.useState(false);
|
|
13573
13575
|
const [draftDateFrom, setDraftDateFrom] = React.useState(dateFrom);
|
|
@@ -13575,7 +13577,7 @@ function DateTimeRangePicker({
|
|
|
13575
13577
|
const [draftTimeFrom, setDraftTimeFrom] = React.useState("12:00 AM");
|
|
13576
13578
|
const [draftTimeTo, setDraftTimeTo] = React.useState("11:59 PM");
|
|
13577
13579
|
const [customRangeError, setCustomRangeError] = React.useState();
|
|
13578
|
-
const datePresetLabel = DATE_PRESETS.find((p) => p.value === preset)?.label ?? "All";
|
|
13580
|
+
const datePresetLabel = DATE_PRESETS$1.find((p) => p.value === preset)?.label ?? "All";
|
|
13579
13581
|
const handlePresetSelect = (value) => {
|
|
13580
13582
|
onPresetChange?.(value);
|
|
13581
13583
|
if (value === "custom") {
|
|
@@ -13586,7 +13588,7 @@ function DateTimeRangePicker({
|
|
|
13586
13588
|
setCustomRangeOpen(true);
|
|
13587
13589
|
return;
|
|
13588
13590
|
}
|
|
13589
|
-
const entry = DATE_PRESETS.find((p) => p.value === value);
|
|
13591
|
+
const entry = DATE_PRESETS$1.find((p) => p.value === value);
|
|
13590
13592
|
if (entry?.ms) {
|
|
13591
13593
|
onDateChange?.(new Date(Date.now() - entry.ms), "from");
|
|
13592
13594
|
onDateChange?.(void 0, "to");
|
|
@@ -15004,6 +15006,4125 @@ function generateDefaultValues(schema) {
|
|
|
15004
15006
|
return generateObjectDefaults(schema.properties, 0);
|
|
15005
15007
|
}
|
|
15006
15008
|
|
|
15009
|
+
const DATE_PRESETS = [
|
|
15010
|
+
{ label: "Last 24 hours", value: "24h" },
|
|
15011
|
+
{ label: "Last 3 days", value: "3d" },
|
|
15012
|
+
{ label: "Last 7 days", value: "7d" },
|
|
15013
|
+
{ label: "Last 14 days", value: "14d" },
|
|
15014
|
+
{ label: "Last 30 days", value: "30d" }
|
|
15015
|
+
];
|
|
15016
|
+
const VALID_PRESETS = new Set(DATE_PRESETS.map((p) => p.value));
|
|
15017
|
+
function isValidPreset(value) {
|
|
15018
|
+
return typeof value === "string" && (VALID_PRESETS.has(value) || value === "custom");
|
|
15019
|
+
}
|
|
15020
|
+
const MetricsContext = React.createContext({
|
|
15021
|
+
datePreset: "24h",
|
|
15022
|
+
setDatePreset: () => {
|
|
15023
|
+
},
|
|
15024
|
+
customRange: void 0,
|
|
15025
|
+
setCustomRange: () => {
|
|
15026
|
+
},
|
|
15027
|
+
dateRangeLabel: "Last 24 hours"
|
|
15028
|
+
});
|
|
15029
|
+
function useMetrics() {
|
|
15030
|
+
return React.useContext(MetricsContext);
|
|
15031
|
+
}
|
|
15032
|
+
function getDateRangeLabel(preset, customRange) {
|
|
15033
|
+
if (preset !== "custom") {
|
|
15034
|
+
return DATE_PRESETS.find((p) => p.value === preset).label;
|
|
15035
|
+
}
|
|
15036
|
+
if (customRange?.from) {
|
|
15037
|
+
if (customRange.to) {
|
|
15038
|
+
return `${dateFns.format(customRange.from, "MMM d, yyyy")} – ${dateFns.format(customRange.to, "MMM d, yyyy")}`;
|
|
15039
|
+
}
|
|
15040
|
+
return dateFns.format(customRange.from, "MMM d, yyyy");
|
|
15041
|
+
}
|
|
15042
|
+
return "Custom range";
|
|
15043
|
+
}
|
|
15044
|
+
function MetricsProvider({
|
|
15045
|
+
children,
|
|
15046
|
+
initialPreset,
|
|
15047
|
+
onPresetChange
|
|
15048
|
+
}) {
|
|
15049
|
+
const [datePreset, setDatePresetState] = React.useState(initialPreset ?? "24h");
|
|
15050
|
+
const [customRange, setCustomRange] = React.useState(void 0);
|
|
15051
|
+
const dateRangeLabel = getDateRangeLabel(datePreset, customRange);
|
|
15052
|
+
React.useEffect(() => {
|
|
15053
|
+
if (initialPreset && initialPreset !== datePreset) {
|
|
15054
|
+
setDatePresetState(initialPreset);
|
|
15055
|
+
}
|
|
15056
|
+
}, [initialPreset]);
|
|
15057
|
+
const setDatePreset = React.useCallback(
|
|
15058
|
+
(v) => {
|
|
15059
|
+
setDatePresetState(v);
|
|
15060
|
+
onPresetChange?.(v);
|
|
15061
|
+
},
|
|
15062
|
+
[onPresetChange]
|
|
15063
|
+
);
|
|
15064
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15065
|
+
MetricsContext.Provider,
|
|
15066
|
+
{
|
|
15067
|
+
value: {
|
|
15068
|
+
datePreset,
|
|
15069
|
+
setDatePreset,
|
|
15070
|
+
customRange,
|
|
15071
|
+
setCustomRange,
|
|
15072
|
+
dateRangeLabel
|
|
15073
|
+
},
|
|
15074
|
+
children
|
|
15075
|
+
}
|
|
15076
|
+
);
|
|
15077
|
+
}
|
|
15078
|
+
|
|
15079
|
+
function DateRangeSelector() {
|
|
15080
|
+
const { datePreset, setDatePreset } = useMetrics();
|
|
15081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15082
|
+
SelectFieldBlock,
|
|
15083
|
+
{
|
|
15084
|
+
name: "date-range",
|
|
15085
|
+
labelIsHidden: true,
|
|
15086
|
+
value: datePreset,
|
|
15087
|
+
options: DATE_PRESETS.map((p) => ({ label: p.label, value: p.value })),
|
|
15088
|
+
onValueChange: (value) => {
|
|
15089
|
+
if (isValidPreset(value)) setDatePreset(value);
|
|
15090
|
+
}
|
|
15091
|
+
}
|
|
15092
|
+
);
|
|
15093
|
+
}
|
|
15094
|
+
|
|
15095
|
+
function formatCompact(n) {
|
|
15096
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
15097
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
|
|
15098
|
+
return n.toLocaleString();
|
|
15099
|
+
}
|
|
15100
|
+
function formatCost(value, unit) {
|
|
15101
|
+
if (unit?.toLowerCase() === "usd" || !unit) {
|
|
15102
|
+
return `$${value < 0.01 && value > 0 ? value.toFixed(4) : value.toFixed(2)}`;
|
|
15103
|
+
}
|
|
15104
|
+
return `${value.toFixed(4)} ${unit}`;
|
|
15105
|
+
}
|
|
15106
|
+
const CHART_COLORS = {
|
|
15107
|
+
green: "#22c55e",
|
|
15108
|
+
orange: "#fb923c",
|
|
15109
|
+
pink: "#f472b6",
|
|
15110
|
+
purple: "#8b5cf6",
|
|
15111
|
+
blue: "#4f83f1",
|
|
15112
|
+
blueDark: "#2b5cd9",
|
|
15113
|
+
blueLight: "#6b8fe5",
|
|
15114
|
+
red: "#f87171",
|
|
15115
|
+
greenDark: "#15613a",
|
|
15116
|
+
redDark: "#991b1b",
|
|
15117
|
+
yellow: "#facc15"
|
|
15118
|
+
};
|
|
15119
|
+
|
|
15120
|
+
function BarListContent({
|
|
15121
|
+
data,
|
|
15122
|
+
maxVal,
|
|
15123
|
+
fmt,
|
|
15124
|
+
color,
|
|
15125
|
+
valueLabel,
|
|
15126
|
+
legend
|
|
15127
|
+
}) {
|
|
15128
|
+
const sorted = [...data].sort((a, b) => b.value - a.value);
|
|
15129
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
15130
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
|
|
15131
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex items-center gap-4", children: legend?.map((l) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
15132
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-2 rounded-full", style: { backgroundColor: l.color } }),
|
|
15133
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-icon2", children: l.label })
|
|
15134
|
+
] }, l.label)) }),
|
|
15135
|
+
valueLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-icon2", children: valueLabel })
|
|
15136
|
+
] }),
|
|
15137
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5", children: sorted.map((d) => {
|
|
15138
|
+
const pct = Math.min(Math.max(maxVal > 0 ? d.value / maxVal * 100 : 0, 0), 100);
|
|
15139
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group flex items-center gap-3", children: [
|
|
15140
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 h-7", children: [
|
|
15141
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15142
|
+
"div",
|
|
15143
|
+
{
|
|
15144
|
+
className: "absolute inset-y-0 left-0 rounded",
|
|
15145
|
+
style: { width: `${pct}%`, backgroundColor: color }
|
|
15146
|
+
}
|
|
15147
|
+
),
|
|
15148
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-y-0 left-2 flex items-center text-xs text-white whitespace-nowrap", children: d.name })
|
|
15149
|
+
] }),
|
|
15150
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs font-mono text-icon6 tabular-nums", children: fmt(d.value) })
|
|
15151
|
+
] }, d.name);
|
|
15152
|
+
}) })
|
|
15153
|
+
] });
|
|
15154
|
+
}
|
|
15155
|
+
function StackedRunsBars({ data }) {
|
|
15156
|
+
const sorted = [...data].sort((a, b) => b.completed + b.errors - (a.completed + a.errors));
|
|
15157
|
+
const maxTotal = Math.max(...sorted.map((d) => d.completed + d.errors));
|
|
15158
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
15159
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 mb-2", children: [
|
|
15160
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex items-center gap-4", children: [
|
|
15161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
15162
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-2 rounded-full", style: { backgroundColor: CHART_COLORS.blue } }),
|
|
15163
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-icon2", children: "Completed" })
|
|
15164
|
+
] }),
|
|
15165
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
15166
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-2 rounded-full", style: { backgroundColor: CHART_COLORS.red } }),
|
|
15167
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-icon2", children: "Errors" })
|
|
15168
|
+
] })
|
|
15169
|
+
] }),
|
|
15170
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-icon2", children: "Total (Success)" })
|
|
15171
|
+
] }),
|
|
15172
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5", children: sorted.map((d) => {
|
|
15173
|
+
const total = d.completed + d.errors;
|
|
15174
|
+
const successPct = total > 0 ? (d.completed / total * 100).toFixed(1) : "0.0";
|
|
15175
|
+
const completedWidth = Math.min(Math.max(maxTotal > 0 ? d.completed / maxTotal * 100 : 0, 0), 100);
|
|
15176
|
+
const errorsWidth = Math.min(Math.max(maxTotal > 0 ? d.errors / maxTotal * 100 : 0, 0), 100);
|
|
15177
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "group flex items-center gap-3", children: [
|
|
15178
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 h-7", children: [
|
|
15179
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
15180
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15181
|
+
"div",
|
|
15182
|
+
{
|
|
15183
|
+
role: "img",
|
|
15184
|
+
"aria-label": `${d.completed.toLocaleString()} completed`,
|
|
15185
|
+
tabIndex: 0,
|
|
15186
|
+
className: "absolute inset-y-0 left-0 rounded-l cursor-default",
|
|
15187
|
+
style: { width: `${completedWidth}%`, backgroundColor: CHART_COLORS.blue }
|
|
15188
|
+
}
|
|
15189
|
+
) }),
|
|
15190
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { side: "top", className: "font-mono", children: [
|
|
15191
|
+
d.completed.toLocaleString(),
|
|
15192
|
+
" completed"
|
|
15193
|
+
] })
|
|
15194
|
+
] }),
|
|
15195
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
15196
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15197
|
+
"div",
|
|
15198
|
+
{
|
|
15199
|
+
role: "img",
|
|
15200
|
+
"aria-label": `${d.errors.toLocaleString()} errors`,
|
|
15201
|
+
tabIndex: 0,
|
|
15202
|
+
className: "absolute inset-y-0 rounded-r cursor-default",
|
|
15203
|
+
style: {
|
|
15204
|
+
left: `${completedWidth}%`,
|
|
15205
|
+
width: `${errorsWidth}%`,
|
|
15206
|
+
backgroundColor: CHART_COLORS.red
|
|
15207
|
+
}
|
|
15208
|
+
}
|
|
15209
|
+
) }),
|
|
15210
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TooltipContent, { side: "top", className: "font-mono", children: [
|
|
15211
|
+
d.errors.toLocaleString(),
|
|
15212
|
+
" errors"
|
|
15213
|
+
] })
|
|
15214
|
+
] }),
|
|
15215
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-y-0 left-2 flex items-center text-xs text-white whitespace-nowrap pointer-events-none", children: d.name })
|
|
15216
|
+
] }),
|
|
15217
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-xs font-mono text-icon6 tabular-nums", children: [
|
|
15218
|
+
total.toLocaleString(),
|
|
15219
|
+
" (",
|
|
15220
|
+
successPct,
|
|
15221
|
+
"%)"
|
|
15222
|
+
] })
|
|
15223
|
+
] }, d.name);
|
|
15224
|
+
}) })
|
|
15225
|
+
] });
|
|
15226
|
+
}
|
|
15227
|
+
|
|
15228
|
+
function KpiCardView({ label, value, prevValue, changePct, isLoading, isError }) {
|
|
15229
|
+
const hasData = value != null;
|
|
15230
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsKpiCard, { children: [
|
|
15231
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.Label, { children: label }),
|
|
15232
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.Value, { className: hasData ? void 0 : "invisible", children: hasData ? value : "—" }),
|
|
15233
|
+
isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.Error, {}) : isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.Loading, {}) : hasData ? changePct != null ? /* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.Change, { changePct, prevValue }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.NoChange, {}) : /* @__PURE__ */ jsxRuntime.jsx(MetricsKpiCard.NoData, {})
|
|
15234
|
+
] });
|
|
15235
|
+
}
|
|
15236
|
+
|
|
15237
|
+
const latencySeries = [
|
|
15238
|
+
{
|
|
15239
|
+
dataKey: "p50",
|
|
15240
|
+
label: "p50",
|
|
15241
|
+
color: CHART_COLORS.blue,
|
|
15242
|
+
aggregate: (data) => ({
|
|
15243
|
+
value: data.length > 0 ? `${Math.round(data.reduce((s, d) => s + d.p50, 0) / data.length)}` : "0",
|
|
15244
|
+
suffix: "avg ms"
|
|
15245
|
+
})
|
|
15246
|
+
},
|
|
15247
|
+
{
|
|
15248
|
+
dataKey: "p95",
|
|
15249
|
+
label: "p95",
|
|
15250
|
+
color: CHART_COLORS.yellow,
|
|
15251
|
+
aggregate: (data) => ({
|
|
15252
|
+
value: data.length > 0 ? `${Math.round(data.reduce((s, d) => s + d.p95, 0) / data.length)}` : "0",
|
|
15253
|
+
suffix: "avg ms"
|
|
15254
|
+
})
|
|
15255
|
+
}
|
|
15256
|
+
];
|
|
15257
|
+
function LatencyChart({ data }) {
|
|
15258
|
+
if (data.length === 0) {
|
|
15259
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No latency data yet" });
|
|
15260
|
+
}
|
|
15261
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MetricsLineChart, { data, series: latencySeries });
|
|
15262
|
+
}
|
|
15263
|
+
function LatencyCardView({ data, isLoading, isError }) {
|
|
15264
|
+
const hasData = !!data && (data.agentData.length > 0 || data.workflowData.length > 0 || data.toolData.length > 0);
|
|
15265
|
+
const p50Values = data ? Object.values(data).filter(Array.isArray).flat().map((d) => d.p50).filter((v) => typeof v === "number") : [];
|
|
15266
|
+
const avgP50 = p50Values.length > 0 ? `${Math.round(p50Values.reduce((s, v) => s + v, 0) / p50Values.length)}ms` : "—";
|
|
15267
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsCard, { children: [
|
|
15268
|
+
/* @__PURE__ */ jsxRuntime.jsxs(MetricsCard.TopBar, { children: [
|
|
15269
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsCard.TitleAndDescription, { title: "Latency", description: "Hourly p50 and p95 latency." }),
|
|
15270
|
+
hasData && /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Summary, { value: avgP50, label: "Avg p50" })
|
|
15271
|
+
] }),
|
|
15272
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Loading, {}) : isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Error, { message: "Failed to load latency data" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Content, { children: !hasData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No latency data yet" }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15273
|
+
Tabs,
|
|
15274
|
+
{
|
|
15275
|
+
defaultTab: data.agentData.length > 0 ? "agents" : data.workflowData.length > 0 ? "workflows" : data.toolData.length > 0 ? "tools" : "agents",
|
|
15276
|
+
className: "overflow-visible",
|
|
15277
|
+
children: [
|
|
15278
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabList, { children: [
|
|
15279
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "agents", children: "Agents" }),
|
|
15280
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "workflows", children: "Workflows" }),
|
|
15281
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "tools", children: "Tools" })
|
|
15282
|
+
] }),
|
|
15283
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "agents", children: /* @__PURE__ */ jsxRuntime.jsx(LatencyChart, { data: data.agentData }) }),
|
|
15284
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "workflows", children: /* @__PURE__ */ jsxRuntime.jsx(LatencyChart, { data: data.workflowData }) }),
|
|
15285
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "tools", children: /* @__PURE__ */ jsxRuntime.jsx(LatencyChart, { data: data.toolData }) })
|
|
15286
|
+
]
|
|
15287
|
+
}
|
|
15288
|
+
) })
|
|
15289
|
+
] });
|
|
15290
|
+
}
|
|
15291
|
+
|
|
15292
|
+
function ModelUsageCostCardView({ rows, isLoading, isError }) {
|
|
15293
|
+
const hasData = !!rows && rows.length > 0;
|
|
15294
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsCard, { children: [
|
|
15295
|
+
/* @__PURE__ */ jsxRuntime.jsxs(MetricsCard.TopBar, { children: [
|
|
15296
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsCard.TitleAndDescription, { title: "Model Usage & Cost", description: "Token consumption by model." }),
|
|
15297
|
+
hasData && (() => {
|
|
15298
|
+
const totalCost = rows.reduce((sum, r) => sum + (r.cost ?? 0), 0);
|
|
15299
|
+
const units = new Set(rows.filter((r) => r.cost != null && r.costUnit).map((r) => r.costUnit));
|
|
15300
|
+
let value;
|
|
15301
|
+
if (units.size === 0) {
|
|
15302
|
+
value = totalCost > 0 ? formatCost(totalCost) : "—";
|
|
15303
|
+
} else if (units.size === 1) {
|
|
15304
|
+
value = totalCost > 0 ? formatCost(totalCost, [...units][0]) : "—";
|
|
15305
|
+
} else {
|
|
15306
|
+
value = "Mixed";
|
|
15307
|
+
}
|
|
15308
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Summary, { value, label: "Total cost" });
|
|
15309
|
+
})()
|
|
15310
|
+
] }),
|
|
15311
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Loading, {}) : isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Error, { message: "Failed to load model usage data" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Content, { children: !hasData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No model usage data yet" }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
15312
|
+
MetricsDataTable,
|
|
15313
|
+
{
|
|
15314
|
+
columns: [
|
|
15315
|
+
{ label: "Model", value: (row) => row.model },
|
|
15316
|
+
{ label: "Input", value: (row) => row.input },
|
|
15317
|
+
{ label: "Output", value: (row) => row.output },
|
|
15318
|
+
{ label: "Cache Read", value: (row) => row.cacheRead },
|
|
15319
|
+
{ label: "Cache Write", value: (row) => row.cacheWrite },
|
|
15320
|
+
{
|
|
15321
|
+
label: "Cost",
|
|
15322
|
+
value: (row) => row.cost != null ? formatCost(row.cost, row.costUnit) : "—",
|
|
15323
|
+
highlight: true
|
|
15324
|
+
}
|
|
15325
|
+
],
|
|
15326
|
+
data: rows.map((row) => ({ ...row, key: row.model }))
|
|
15327
|
+
}
|
|
15328
|
+
) })
|
|
15329
|
+
] });
|
|
15330
|
+
}
|
|
15331
|
+
|
|
15332
|
+
const SERIES_COLORS = [
|
|
15333
|
+
CHART_COLORS.green,
|
|
15334
|
+
CHART_COLORS.blue,
|
|
15335
|
+
CHART_COLORS.purple,
|
|
15336
|
+
CHART_COLORS.orange,
|
|
15337
|
+
CHART_COLORS.pink,
|
|
15338
|
+
CHART_COLORS.yellow
|
|
15339
|
+
];
|
|
15340
|
+
function ScoresCardView({ data, isLoading, isError }) {
|
|
15341
|
+
const hasData = !!data && (data.summaryData.length > 0 || data.overTimeData.length > 0);
|
|
15342
|
+
const series = React.useMemo(() => {
|
|
15343
|
+
if (!data?.scorerNames) return [];
|
|
15344
|
+
return data.scorerNames.map((name, i) => ({
|
|
15345
|
+
dataKey: name,
|
|
15346
|
+
label: name,
|
|
15347
|
+
color: SERIES_COLORS[i % SERIES_COLORS.length],
|
|
15348
|
+
aggregate: (points) => ({
|
|
15349
|
+
value: points.length > 0 ? (points.reduce((s, d) => s + (d[name] ?? 0), 0) / points.length).toFixed(2) : "0",
|
|
15350
|
+
suffix: "avg"
|
|
15351
|
+
})
|
|
15352
|
+
}));
|
|
15353
|
+
}, [data?.scorerNames]);
|
|
15354
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsCard, { children: [
|
|
15355
|
+
/* @__PURE__ */ jsxRuntime.jsxs(MetricsCard.TopBar, { children: [
|
|
15356
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsCard.TitleAndDescription, { title: "Scores", description: "Evaluation scorer performance." }),
|
|
15357
|
+
hasData && /* @__PURE__ */ jsxRuntime.jsx(
|
|
15358
|
+
MetricsCard.Summary,
|
|
15359
|
+
{
|
|
15360
|
+
value: data?.avgScore != null ? `avg ${data.avgScore}` : "—",
|
|
15361
|
+
label: "Across all scorers"
|
|
15362
|
+
}
|
|
15363
|
+
)
|
|
15364
|
+
] }),
|
|
15365
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Loading, {}) : isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Error, { message: "Failed to load scores data" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Content, { children: !hasData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No scores data yet" }) : /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { defaultTab: "over-time", className: "overflow-visible", children: [
|
|
15366
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabList, { children: [
|
|
15367
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "over-time", children: "Over Time" }),
|
|
15368
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "summary", children: "Summary" })
|
|
15369
|
+
] }),
|
|
15370
|
+
/* @__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" }) }),
|
|
15371
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "summary", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15372
|
+
MetricsDataTable,
|
|
15373
|
+
{
|
|
15374
|
+
columns: [
|
|
15375
|
+
{ label: "Scorer", value: (row) => row.scorer },
|
|
15376
|
+
{ label: "Avg", value: (row) => row.avg.toFixed(2), highlight: true },
|
|
15377
|
+
{ label: "Min", value: (row) => row.min.toFixed(2) },
|
|
15378
|
+
{ label: "Max", value: (row) => row.max.toFixed(2) },
|
|
15379
|
+
{ label: "Count", value: (row) => row.count.toLocaleString() }
|
|
15380
|
+
],
|
|
15381
|
+
data: data.summaryData.map((row) => ({ ...row, key: row.scorer }))
|
|
15382
|
+
}
|
|
15383
|
+
) })
|
|
15384
|
+
] }) })
|
|
15385
|
+
] });
|
|
15386
|
+
}
|
|
15387
|
+
|
|
15388
|
+
function isTokenUsageTab(value) {
|
|
15389
|
+
return value === "tokens" || value === "cost";
|
|
15390
|
+
}
|
|
15391
|
+
function TokenUsageByAgentCardView({ data, isLoading, isError }) {
|
|
15392
|
+
const [activeTab, setActiveTab] = React.useState("tokens");
|
|
15393
|
+
const rows = data ?? [];
|
|
15394
|
+
const hasData = rows.length > 0;
|
|
15395
|
+
const totalTokens = rows.reduce((s, d) => s + d.total, 0);
|
|
15396
|
+
const costRows = rows.filter((d) => d.cost != null && d.cost > 0);
|
|
15397
|
+
const uniqueCostUnits = new Set(costRows.map((d) => d.costUnit ?? "usd"));
|
|
15398
|
+
const hasSingleCostUnit = uniqueCostUnits.size <= 1;
|
|
15399
|
+
const costUnit = hasSingleCostUnit ? [...uniqueCostUnits][0] ?? "usd" : null;
|
|
15400
|
+
const totalCost = hasSingleCostUnit ? costRows.reduce((s, d) => s + (d.cost ?? 0), 0) : 0;
|
|
15401
|
+
const hasCostData = hasSingleCostUnit && totalCost > 0;
|
|
15402
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsCard, { children: [
|
|
15403
|
+
/* @__PURE__ */ jsxRuntime.jsxs(MetricsCard.TopBar, { children: [
|
|
15404
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15405
|
+
MetricsCard.TitleAndDescription,
|
|
15406
|
+
{
|
|
15407
|
+
title: "Token Usage by Agent",
|
|
15408
|
+
description: "Token consumption grouped by agent."
|
|
15409
|
+
}
|
|
15410
|
+
),
|
|
15411
|
+
hasData && (activeTab === "cost" && hasCostData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Summary, { value: formatCost(totalCost, costUnit), label: "Total cost" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Summary, { value: formatCompact(totalTokens), label: "Total tokens" }))
|
|
15412
|
+
] }),
|
|
15413
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Loading, {}) : isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Error, { message: "Failed to load token usage data" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Content, { children: !hasData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No token usage data yet" }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15414
|
+
Tabs,
|
|
15415
|
+
{
|
|
15416
|
+
defaultTab: "tokens",
|
|
15417
|
+
value: activeTab,
|
|
15418
|
+
onValueChange: (v) => {
|
|
15419
|
+
if (isTokenUsageTab(v)) setActiveTab(v);
|
|
15420
|
+
},
|
|
15421
|
+
className: "grid grid-rows-[auto_1fr] overflow-y-auto h-full",
|
|
15422
|
+
children: [
|
|
15423
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabList, { children: [
|
|
15424
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "tokens", children: "Tokens" }),
|
|
15425
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "cost", children: "Cost" })
|
|
15426
|
+
] }),
|
|
15427
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "tokens", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15428
|
+
HorizontalBars,
|
|
15429
|
+
{
|
|
15430
|
+
data: rows.map((d) => ({ name: d.name, values: [d.input, d.output] })),
|
|
15431
|
+
segments: [
|
|
15432
|
+
{ label: "Input", color: CHART_COLORS.blueDark },
|
|
15433
|
+
{ label: "Output", color: CHART_COLORS.blue }
|
|
15434
|
+
],
|
|
15435
|
+
maxVal: Math.max(...rows.map((d) => d.input + d.output)),
|
|
15436
|
+
fmt: formatCompact
|
|
15437
|
+
}
|
|
15438
|
+
) }),
|
|
15439
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "cost", children: hasCostData ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
15440
|
+
HorizontalBars,
|
|
15441
|
+
{
|
|
15442
|
+
data: costRows.slice().sort((a, b) => (b.cost ?? 0) - (a.cost ?? 0)).map((d) => ({ name: d.name, values: [d.cost] })),
|
|
15443
|
+
segments: [{ label: "Cost", color: CHART_COLORS.purple }],
|
|
15444
|
+
maxVal: Math.max(...costRows.map((d) => d.cost ?? 0)),
|
|
15445
|
+
fmt: (v) => formatCost(v, costUnit)
|
|
15446
|
+
}
|
|
15447
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No cost data yet" }) })
|
|
15448
|
+
]
|
|
15449
|
+
}
|
|
15450
|
+
) })
|
|
15451
|
+
] });
|
|
15452
|
+
}
|
|
15453
|
+
|
|
15454
|
+
function VolumeBars({ data }) {
|
|
15455
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15456
|
+
HorizontalBars,
|
|
15457
|
+
{
|
|
15458
|
+
data: data.map((d) => ({ name: d.name, values: [d.completed, d.errors] })),
|
|
15459
|
+
segments: [
|
|
15460
|
+
{ label: "Completed", color: CHART_COLORS.blueDark },
|
|
15461
|
+
{ label: "Errors", color: CHART_COLORS.pink }
|
|
15462
|
+
],
|
|
15463
|
+
maxVal: Math.max(...data.map((d) => d.completed + d.errors)),
|
|
15464
|
+
fmt: formatCompact
|
|
15465
|
+
}
|
|
15466
|
+
);
|
|
15467
|
+
}
|
|
15468
|
+
function TracesVolumeCardView({ data, isLoading, isError }) {
|
|
15469
|
+
const hasData = !!data && (data.agentData.length > 0 || data.workflowData.length > 0 || data.toolData.length > 0);
|
|
15470
|
+
const total = data ? [...data.agentData, ...data.workflowData, ...data.toolData].reduce((s, d) => s + d.completed + d.errors, 0) : 0;
|
|
15471
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(MetricsCard, { children: [
|
|
15472
|
+
/* @__PURE__ */ jsxRuntime.jsxs(MetricsCard.TopBar, { children: [
|
|
15473
|
+
/* @__PURE__ */ jsxRuntime.jsx(MetricsCard.TitleAndDescription, { title: "Trace Volume", description: "Runs and call counts." }),
|
|
15474
|
+
hasData && /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Summary, { value: formatCompact(total), label: "Total runs" })
|
|
15475
|
+
] }),
|
|
15476
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Loading, {}) : isError ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Error, { message: "Failed to load trace volume data" }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.Content, { children: !hasData ? /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No trace volume data yet" }) : /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { defaultTab: "agents", className: "grid grid-rows-[auto_1fr] overflow-y-auto h-full", children: [
|
|
15477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabList, { children: [
|
|
15478
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "agents", children: "Agents" }),
|
|
15479
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "workflows", children: "Workflows" }),
|
|
15480
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "tools", children: "Tools" })
|
|
15481
|
+
] }),
|
|
15482
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "agents", children: data.agentData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeBars, { data: data.agentData }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No agent data yet" }) }),
|
|
15483
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "workflows", children: data.workflowData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeBars, { data: data.workflowData }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No workflow data yet" }) }),
|
|
15484
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "tools", children: data.toolData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(VolumeBars, { data: data.toolData }) : /* @__PURE__ */ jsxRuntime.jsx(MetricsCard.NoData, { message: "No tool data yet" }) })
|
|
15485
|
+
] }) })
|
|
15486
|
+
] });
|
|
15487
|
+
}
|
|
15488
|
+
|
|
15489
|
+
const PRESET_MS$2 = {
|
|
15490
|
+
"24h": 24 * 60 * 60 * 1e3,
|
|
15491
|
+
"3d": 3 * 24 * 60 * 60 * 1e3,
|
|
15492
|
+
"7d": 7 * 24 * 60 * 60 * 1e3,
|
|
15493
|
+
"14d": 14 * 24 * 60 * 60 * 1e3,
|
|
15494
|
+
"30d": 30 * 24 * 60 * 60 * 1e3
|
|
15495
|
+
};
|
|
15496
|
+
function buildTimestamp(preset, customRange) {
|
|
15497
|
+
const now = /* @__PURE__ */ new Date();
|
|
15498
|
+
if (preset !== "custom") {
|
|
15499
|
+
const ms = PRESET_MS$2[preset] ?? PRESET_MS$2["24h"];
|
|
15500
|
+
return { start: new Date(now.getTime() - ms), end: now };
|
|
15501
|
+
}
|
|
15502
|
+
return {
|
|
15503
|
+
start: customRange?.from ?? new Date(now.getTime() - PRESET_MS$2["24h"]),
|
|
15504
|
+
end: customRange?.to ?? now
|
|
15505
|
+
};
|
|
15506
|
+
}
|
|
15507
|
+
function useMetricsFilters() {
|
|
15508
|
+
const { datePreset, customRange } = useMetrics();
|
|
15509
|
+
const timestamp = buildTimestamp(datePreset, customRange);
|
|
15510
|
+
return { datePreset, customRange, timestamp };
|
|
15511
|
+
}
|
|
15512
|
+
|
|
15513
|
+
function useAgentRunsKpiMetrics() {
|
|
15514
|
+
const client = react.useMastraClient();
|
|
15515
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15516
|
+
return reactQuery.useQuery({
|
|
15517
|
+
queryKey: ["metrics", "agent-runs-kpi", datePreset, customRange],
|
|
15518
|
+
queryFn: () => client.getMetricAggregate({
|
|
15519
|
+
name: ["mastra_agent_duration_ms"],
|
|
15520
|
+
aggregation: "count",
|
|
15521
|
+
filters: { timestamp },
|
|
15522
|
+
comparePeriod: "previous_period"
|
|
15523
|
+
})
|
|
15524
|
+
});
|
|
15525
|
+
}
|
|
15526
|
+
|
|
15527
|
+
function useAvgScoreKpiMetrics() {
|
|
15528
|
+
const client = react.useMastraClient();
|
|
15529
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15530
|
+
return reactQuery.useQuery({
|
|
15531
|
+
queryKey: ["metrics", "avg-score-kpi", datePreset, customRange],
|
|
15532
|
+
queryFn: async () => {
|
|
15533
|
+
const scorersMap = await client.listScorers();
|
|
15534
|
+
const scorerIds = Object.keys(scorersMap ?? {});
|
|
15535
|
+
if (scorerIds.length === 0) {
|
|
15536
|
+
return { value: null, previousValue: null, changePercent: null };
|
|
15537
|
+
}
|
|
15538
|
+
const filters = {
|
|
15539
|
+
timestamp: { start: timestamp.start, end: timestamp.end }
|
|
15540
|
+
};
|
|
15541
|
+
const results = await Promise.all(
|
|
15542
|
+
scorerIds.map(async (scorerId) => {
|
|
15543
|
+
const [avg2, count] = await Promise.all([
|
|
15544
|
+
client.getScoreAggregate({ scorerId, aggregation: "avg", filters }),
|
|
15545
|
+
client.getScoreAggregate({ scorerId, aggregation: "count", filters })
|
|
15546
|
+
]);
|
|
15547
|
+
return { avg: avg2.value ?? 0, count: count.value ?? 0 };
|
|
15548
|
+
})
|
|
15549
|
+
);
|
|
15550
|
+
const withData = results.filter((r) => r.count > 0);
|
|
15551
|
+
if (withData.length === 0) {
|
|
15552
|
+
return { value: null, previousValue: null, changePercent: null };
|
|
15553
|
+
}
|
|
15554
|
+
const totalCount = withData.reduce((sum, r) => sum + r.count, 0);
|
|
15555
|
+
const weightedSum = withData.reduce((sum, r) => sum + r.avg * r.count, 0);
|
|
15556
|
+
const avg = weightedSum / totalCount;
|
|
15557
|
+
return { value: Math.round(avg * 100) / 100, previousValue: null, changePercent: null };
|
|
15558
|
+
}
|
|
15559
|
+
});
|
|
15560
|
+
}
|
|
15561
|
+
|
|
15562
|
+
function useModelCostKpiMetrics() {
|
|
15563
|
+
const client = react.useMastraClient();
|
|
15564
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15565
|
+
return reactQuery.useQuery({
|
|
15566
|
+
queryKey: ["metrics", "model-cost-kpi", datePreset, customRange],
|
|
15567
|
+
queryFn: async () => {
|
|
15568
|
+
const res = await client.getMetricAggregate({
|
|
15569
|
+
name: ["mastra_model_total_input_tokens", "mastra_model_total_output_tokens"],
|
|
15570
|
+
aggregation: "sum",
|
|
15571
|
+
filters: { timestamp },
|
|
15572
|
+
comparePeriod: "previous_period"
|
|
15573
|
+
});
|
|
15574
|
+
return {
|
|
15575
|
+
cost: res.estimatedCost ?? null,
|
|
15576
|
+
costUnit: res.costUnit ?? null,
|
|
15577
|
+
previousCost: res.previousEstimatedCost ?? null,
|
|
15578
|
+
costChangePercent: res.costChangePercent ?? null
|
|
15579
|
+
};
|
|
15580
|
+
}
|
|
15581
|
+
});
|
|
15582
|
+
}
|
|
15583
|
+
|
|
15584
|
+
function useTotalTokensKpiMetrics() {
|
|
15585
|
+
const client = react.useMastraClient();
|
|
15586
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15587
|
+
return reactQuery.useQuery({
|
|
15588
|
+
queryKey: ["metrics", "total-tokens-kpi", datePreset, customRange],
|
|
15589
|
+
queryFn: async () => {
|
|
15590
|
+
const [input, output] = await Promise.all([
|
|
15591
|
+
client.getMetricAggregate({
|
|
15592
|
+
name: ["mastra_model_total_input_tokens"],
|
|
15593
|
+
aggregation: "sum",
|
|
15594
|
+
filters: { timestamp },
|
|
15595
|
+
comparePeriod: "previous_period"
|
|
15596
|
+
}),
|
|
15597
|
+
client.getMetricAggregate({
|
|
15598
|
+
name: ["mastra_model_total_output_tokens"],
|
|
15599
|
+
aggregation: "sum",
|
|
15600
|
+
filters: { timestamp },
|
|
15601
|
+
comparePeriod: "previous_period"
|
|
15602
|
+
})
|
|
15603
|
+
]);
|
|
15604
|
+
const hasCurrent = input.value != null || output.value != null;
|
|
15605
|
+
const hasPrevious = input.previousValue != null || output.previousValue != null;
|
|
15606
|
+
const value = (input.value ?? 0) + (output.value ?? 0);
|
|
15607
|
+
const previousValue = (input.previousValue ?? 0) + (output.previousValue ?? 0);
|
|
15608
|
+
const changePercent = hasPrevious && previousValue > 0 ? (value - previousValue) / previousValue * 100 : null;
|
|
15609
|
+
return {
|
|
15610
|
+
value: hasCurrent ? value : null,
|
|
15611
|
+
previousValue: hasPrevious ? previousValue : null,
|
|
15612
|
+
changePercent
|
|
15613
|
+
};
|
|
15614
|
+
}
|
|
15615
|
+
});
|
|
15616
|
+
}
|
|
15617
|
+
|
|
15618
|
+
function useModelUsageCostMetrics() {
|
|
15619
|
+
const client = react.useMastraClient();
|
|
15620
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15621
|
+
return reactQuery.useQuery({
|
|
15622
|
+
queryKey: ["metrics", "model-usage-cost", datePreset, customRange],
|
|
15623
|
+
queryFn: async () => {
|
|
15624
|
+
const metrics = [
|
|
15625
|
+
"mastra_model_total_input_tokens",
|
|
15626
|
+
"mastra_model_total_output_tokens",
|
|
15627
|
+
"mastra_model_input_cache_read_tokens",
|
|
15628
|
+
"mastra_model_input_cache_write_tokens"
|
|
15629
|
+
];
|
|
15630
|
+
const [inputRes, outputRes, cacheReadRes, cacheWriteRes] = await Promise.all(
|
|
15631
|
+
metrics.map(
|
|
15632
|
+
(name) => client.getMetricBreakdown({
|
|
15633
|
+
name: [name],
|
|
15634
|
+
groupBy: ["model"],
|
|
15635
|
+
aggregation: "sum",
|
|
15636
|
+
filters: { timestamp }
|
|
15637
|
+
})
|
|
15638
|
+
)
|
|
15639
|
+
);
|
|
15640
|
+
const modelMap = /* @__PURE__ */ new Map();
|
|
15641
|
+
const ensureModel = (model) => {
|
|
15642
|
+
if (!modelMap.has(model)) {
|
|
15643
|
+
modelMap.set(model, { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: null, costUnit: null });
|
|
15644
|
+
}
|
|
15645
|
+
return modelMap.get(model);
|
|
15646
|
+
};
|
|
15647
|
+
const addCost = (entry, group) => {
|
|
15648
|
+
if (group.estimatedCost != null) {
|
|
15649
|
+
entry.cost = (entry.cost ?? 0) + group.estimatedCost;
|
|
15650
|
+
if (group.costUnit) entry.costUnit = group.costUnit;
|
|
15651
|
+
}
|
|
15652
|
+
};
|
|
15653
|
+
for (const group of inputRes.groups) {
|
|
15654
|
+
const m = group.dimensions.model ?? "unknown";
|
|
15655
|
+
const entry = ensureModel(m);
|
|
15656
|
+
entry.input = group.value;
|
|
15657
|
+
addCost(entry, group);
|
|
15658
|
+
}
|
|
15659
|
+
for (const group of outputRes.groups) {
|
|
15660
|
+
const m = group.dimensions.model ?? "unknown";
|
|
15661
|
+
const entry = ensureModel(m);
|
|
15662
|
+
entry.output = group.value;
|
|
15663
|
+
addCost(entry, group);
|
|
15664
|
+
}
|
|
15665
|
+
for (const group of cacheReadRes.groups) {
|
|
15666
|
+
const m = group.dimensions.model ?? "unknown";
|
|
15667
|
+
const entry = ensureModel(m);
|
|
15668
|
+
entry.cacheRead = group.value;
|
|
15669
|
+
addCost(entry, group);
|
|
15670
|
+
}
|
|
15671
|
+
for (const group of cacheWriteRes.groups) {
|
|
15672
|
+
const m = group.dimensions.model ?? "unknown";
|
|
15673
|
+
const entry = ensureModel(m);
|
|
15674
|
+
entry.cacheWrite = group.value;
|
|
15675
|
+
addCost(entry, group);
|
|
15676
|
+
}
|
|
15677
|
+
return Array.from(modelMap.entries()).map(([model, vals]) => ({
|
|
15678
|
+
model,
|
|
15679
|
+
input: formatCompact(vals.input),
|
|
15680
|
+
output: formatCompact(vals.output),
|
|
15681
|
+
cacheRead: formatCompact(vals.cacheRead),
|
|
15682
|
+
cacheWrite: formatCompact(vals.cacheWrite),
|
|
15683
|
+
cost: vals.cost,
|
|
15684
|
+
costUnit: vals.costUnit
|
|
15685
|
+
})).sort((a, b) => a.model.localeCompare(b.model));
|
|
15686
|
+
}
|
|
15687
|
+
});
|
|
15688
|
+
}
|
|
15689
|
+
|
|
15690
|
+
async function fetchPercentiles(client, metricName, timestamp) {
|
|
15691
|
+
const res = await client.getMetricPercentiles({
|
|
15692
|
+
name: metricName,
|
|
15693
|
+
percentiles: [0.5, 0.95],
|
|
15694
|
+
interval: "1h",
|
|
15695
|
+
filters: { timestamp }
|
|
15696
|
+
});
|
|
15697
|
+
const p50Series = res.series.find((s) => s.percentile === 0.5);
|
|
15698
|
+
const p95Series = res.series.find((s) => s.percentile === 0.95);
|
|
15699
|
+
if (!p50Series || !p95Series) return [];
|
|
15700
|
+
const p95Map = new Map(p95Series.points.map((p) => [new Date(p.timestamp).getTime(), p.value]));
|
|
15701
|
+
return p50Series.points.map((p) => {
|
|
15702
|
+
const ts = new Date(p.timestamp);
|
|
15703
|
+
return {
|
|
15704
|
+
time: ts.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
|
|
15705
|
+
p50: Math.round(p.value),
|
|
15706
|
+
p95: Math.round(p95Map.get(ts.getTime()) ?? 0)
|
|
15707
|
+
};
|
|
15708
|
+
});
|
|
15709
|
+
}
|
|
15710
|
+
function useLatencyMetrics() {
|
|
15711
|
+
const client = react.useMastraClient();
|
|
15712
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15713
|
+
return reactQuery.useQuery({
|
|
15714
|
+
queryKey: ["metrics", "latency", datePreset, customRange],
|
|
15715
|
+
queryFn: async () => {
|
|
15716
|
+
const [agentData, workflowData, toolData] = await Promise.all([
|
|
15717
|
+
fetchPercentiles(client, "mastra_agent_duration_ms", timestamp),
|
|
15718
|
+
fetchPercentiles(client, "mastra_workflow_duration_ms", timestamp),
|
|
15719
|
+
fetchPercentiles(client, "mastra_tool_duration_ms", timestamp)
|
|
15720
|
+
]);
|
|
15721
|
+
return { agentData, workflowData, toolData };
|
|
15722
|
+
}
|
|
15723
|
+
});
|
|
15724
|
+
}
|
|
15725
|
+
|
|
15726
|
+
async function fetchVolume(client, metricName, timestamp) {
|
|
15727
|
+
const res = await client.getMetricBreakdown({
|
|
15728
|
+
name: [metricName],
|
|
15729
|
+
groupBy: ["entityName", "status"],
|
|
15730
|
+
aggregation: "count",
|
|
15731
|
+
filters: { timestamp }
|
|
15732
|
+
});
|
|
15733
|
+
const map = /* @__PURE__ */ new Map();
|
|
15734
|
+
for (const group of res.groups) {
|
|
15735
|
+
const name = group.dimensions.entityName ?? "unknown";
|
|
15736
|
+
const status = group.dimensions.status ?? "ok";
|
|
15737
|
+
if (!map.has(name)) {
|
|
15738
|
+
map.set(name, { completed: 0, errors: 0 });
|
|
15739
|
+
}
|
|
15740
|
+
const entry = map.get(name);
|
|
15741
|
+
if (status === "error") {
|
|
15742
|
+
entry.errors += group.value;
|
|
15743
|
+
} else {
|
|
15744
|
+
entry.completed += group.value;
|
|
15745
|
+
}
|
|
15746
|
+
}
|
|
15747
|
+
return Array.from(map.entries()).map(([name, vals]) => ({ name, ...vals })).sort((a, b) => b.completed + b.errors - (a.completed + a.errors));
|
|
15748
|
+
}
|
|
15749
|
+
function useTraceVolumeMetrics() {
|
|
15750
|
+
const client = react.useMastraClient();
|
|
15751
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15752
|
+
return reactQuery.useQuery({
|
|
15753
|
+
queryKey: ["metrics", "trace-volume", datePreset, customRange],
|
|
15754
|
+
queryFn: async () => {
|
|
15755
|
+
const [agentData, workflowData, toolData] = await Promise.all([
|
|
15756
|
+
fetchVolume(client, "mastra_agent_duration_ms", timestamp),
|
|
15757
|
+
fetchVolume(client, "mastra_workflow_duration_ms", timestamp),
|
|
15758
|
+
fetchVolume(client, "mastra_tool_duration_ms", timestamp)
|
|
15759
|
+
]);
|
|
15760
|
+
return { agentData, workflowData, toolData };
|
|
15761
|
+
}
|
|
15762
|
+
});
|
|
15763
|
+
}
|
|
15764
|
+
|
|
15765
|
+
function useScoresMetrics() {
|
|
15766
|
+
const client = react.useMastraClient();
|
|
15767
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15768
|
+
return reactQuery.useQuery({
|
|
15769
|
+
queryKey: ["metrics", "scores-card", datePreset, customRange],
|
|
15770
|
+
queryFn: async () => {
|
|
15771
|
+
const filters = {
|
|
15772
|
+
timestamp: { start: timestamp.start, end: timestamp.end }
|
|
15773
|
+
};
|
|
15774
|
+
const scorersMap = await client.listScorers();
|
|
15775
|
+
const scorerIds = Object.keys(scorersMap ?? {});
|
|
15776
|
+
if (scorerIds.length === 0) {
|
|
15777
|
+
return { summaryData: [], overTimeData: [], scorerNames: [], avgScore: null };
|
|
15778
|
+
}
|
|
15779
|
+
const summaryResults = await Promise.all(
|
|
15780
|
+
scorerIds.map(async (scorerId) => {
|
|
15781
|
+
const [avg, min, max, count] = await Promise.all([
|
|
15782
|
+
client.getScoreAggregate({ scorerId, aggregation: "avg", filters }),
|
|
15783
|
+
client.getScoreAggregate({ scorerId, aggregation: "min", filters }),
|
|
15784
|
+
client.getScoreAggregate({ scorerId, aggregation: "max", filters }),
|
|
15785
|
+
client.getScoreAggregate({ scorerId, aggregation: "count", filters })
|
|
15786
|
+
]);
|
|
15787
|
+
return {
|
|
15788
|
+
scorer: scorerId,
|
|
15789
|
+
avg: avg.value ?? 0,
|
|
15790
|
+
min: min.value ?? 0,
|
|
15791
|
+
max: max.value ?? 0,
|
|
15792
|
+
count: count.value ?? 0
|
|
15793
|
+
};
|
|
15794
|
+
})
|
|
15795
|
+
);
|
|
15796
|
+
const summaryData = summaryResults.filter((s) => s.count > 0);
|
|
15797
|
+
const scorerNames = summaryData.map((s) => s.scorer);
|
|
15798
|
+
if (summaryData.length === 0) {
|
|
15799
|
+
return { summaryData: [], overTimeData: [], scorerNames: [], avgScore: null };
|
|
15800
|
+
}
|
|
15801
|
+
const totalWeighted = summaryData.reduce((s, d) => s + d.avg * d.count, 0);
|
|
15802
|
+
const totalCount = summaryData.reduce((s, d) => s + d.count, 0);
|
|
15803
|
+
const avgScore = totalCount ? Math.round(totalWeighted / totalCount * 100) / 100 : 0;
|
|
15804
|
+
const interval = "1h";
|
|
15805
|
+
const timeSeriesResults = await Promise.all(
|
|
15806
|
+
scorerNames.map(
|
|
15807
|
+
(scorerId) => client.getScoreTimeSeries({
|
|
15808
|
+
scorerId,
|
|
15809
|
+
interval,
|
|
15810
|
+
aggregation: "avg",
|
|
15811
|
+
filters
|
|
15812
|
+
})
|
|
15813
|
+
)
|
|
15814
|
+
);
|
|
15815
|
+
const hourBuckets = /* @__PURE__ */ new Map();
|
|
15816
|
+
for (let i = 0; i < scorerNames.length; i++) {
|
|
15817
|
+
const scorerId = scorerNames[i];
|
|
15818
|
+
const series = timeSeriesResults[i]?.series ?? [];
|
|
15819
|
+
for (const s of series) {
|
|
15820
|
+
for (const point of s.points) {
|
|
15821
|
+
const ts = new Date(point.timestamp);
|
|
15822
|
+
const hourKey = ts.toLocaleTimeString("en-US", {
|
|
15823
|
+
hour: "2-digit",
|
|
15824
|
+
minute: "2-digit",
|
|
15825
|
+
hour12: false
|
|
15826
|
+
});
|
|
15827
|
+
if (!hourBuckets.has(hourKey)) {
|
|
15828
|
+
hourBuckets.set(hourKey, /* @__PURE__ */ new Map());
|
|
15829
|
+
}
|
|
15830
|
+
const scorerMap = hourBuckets.get(hourKey);
|
|
15831
|
+
if (!scorerMap.has(scorerId)) {
|
|
15832
|
+
scorerMap.set(scorerId, { sum: 0, count: 0 });
|
|
15833
|
+
}
|
|
15834
|
+
const acc = scorerMap.get(scorerId);
|
|
15835
|
+
acc.sum += point.value;
|
|
15836
|
+
acc.count += 1;
|
|
15837
|
+
}
|
|
15838
|
+
}
|
|
15839
|
+
}
|
|
15840
|
+
const overTimeData = Array.from(hourBuckets.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([hourKey, scorerMap]) => {
|
|
15841
|
+
const point = { time: hourKey };
|
|
15842
|
+
for (const [scorerId, acc] of scorerMap) {
|
|
15843
|
+
point[scorerId] = +(acc.sum / acc.count).toFixed(2);
|
|
15844
|
+
}
|
|
15845
|
+
return point;
|
|
15846
|
+
});
|
|
15847
|
+
return {
|
|
15848
|
+
summaryData,
|
|
15849
|
+
overTimeData,
|
|
15850
|
+
scorerNames,
|
|
15851
|
+
avgScore
|
|
15852
|
+
};
|
|
15853
|
+
}
|
|
15854
|
+
});
|
|
15855
|
+
}
|
|
15856
|
+
|
|
15857
|
+
function useTokenUsageByAgentMetrics() {
|
|
15858
|
+
const client = react.useMastraClient();
|
|
15859
|
+
const { datePreset, customRange, timestamp } = useMetricsFilters();
|
|
15860
|
+
return reactQuery.useQuery({
|
|
15861
|
+
queryKey: ["metrics", "token-usage-by-agent", datePreset, customRange],
|
|
15862
|
+
queryFn: async () => {
|
|
15863
|
+
const [inputRes, outputRes, cacheReadRes, cacheWriteRes] = await Promise.all([
|
|
15864
|
+
client.getMetricBreakdown({
|
|
15865
|
+
name: ["mastra_model_total_input_tokens"],
|
|
15866
|
+
groupBy: ["entityName"],
|
|
15867
|
+
aggregation: "sum",
|
|
15868
|
+
filters: { timestamp }
|
|
15869
|
+
}),
|
|
15870
|
+
client.getMetricBreakdown({
|
|
15871
|
+
name: ["mastra_model_total_output_tokens"],
|
|
15872
|
+
groupBy: ["entityName"],
|
|
15873
|
+
aggregation: "sum",
|
|
15874
|
+
filters: { timestamp }
|
|
15875
|
+
}),
|
|
15876
|
+
client.getMetricBreakdown({
|
|
15877
|
+
name: ["mastra_model_input_cache_read_tokens"],
|
|
15878
|
+
groupBy: ["entityName"],
|
|
15879
|
+
aggregation: "sum",
|
|
15880
|
+
filters: { timestamp }
|
|
15881
|
+
}),
|
|
15882
|
+
client.getMetricBreakdown({
|
|
15883
|
+
name: ["mastra_model_input_cache_write_tokens"],
|
|
15884
|
+
groupBy: ["entityName"],
|
|
15885
|
+
aggregation: "sum",
|
|
15886
|
+
filters: { timestamp }
|
|
15887
|
+
})
|
|
15888
|
+
]);
|
|
15889
|
+
const agentMap = /* @__PURE__ */ new Map();
|
|
15890
|
+
const ensure = (name) => {
|
|
15891
|
+
if (!agentMap.has(name)) {
|
|
15892
|
+
agentMap.set(name, { input: 0, output: 0, cost: null, costUnit: null });
|
|
15893
|
+
}
|
|
15894
|
+
return agentMap.get(name);
|
|
15895
|
+
};
|
|
15896
|
+
const addCost = (entry, group) => {
|
|
15897
|
+
if (group.estimatedCost != null) {
|
|
15898
|
+
entry.cost = (entry.cost ?? 0) + group.estimatedCost;
|
|
15899
|
+
if (group.costUnit) entry.costUnit = group.costUnit;
|
|
15900
|
+
}
|
|
15901
|
+
};
|
|
15902
|
+
for (const group of inputRes.groups) {
|
|
15903
|
+
const name = group.dimensions.entityName ?? "unknown";
|
|
15904
|
+
const entry = ensure(name);
|
|
15905
|
+
entry.input = group.value;
|
|
15906
|
+
addCost(entry, group);
|
|
15907
|
+
}
|
|
15908
|
+
for (const group of outputRes.groups) {
|
|
15909
|
+
const name = group.dimensions.entityName ?? "unknown";
|
|
15910
|
+
const entry = ensure(name);
|
|
15911
|
+
entry.output = group.value;
|
|
15912
|
+
addCost(entry, group);
|
|
15913
|
+
}
|
|
15914
|
+
for (const group of cacheReadRes.groups) {
|
|
15915
|
+
const name = group.dimensions.entityName ?? "unknown";
|
|
15916
|
+
const entry = ensure(name);
|
|
15917
|
+
addCost(entry, group);
|
|
15918
|
+
}
|
|
15919
|
+
for (const group of cacheWriteRes.groups) {
|
|
15920
|
+
const name = group.dimensions.entityName ?? "unknown";
|
|
15921
|
+
const entry = ensure(name);
|
|
15922
|
+
addCost(entry, group);
|
|
15923
|
+
}
|
|
15924
|
+
return Array.from(agentMap.entries()).map(([name, vals]) => ({
|
|
15925
|
+
name,
|
|
15926
|
+
input: vals.input,
|
|
15927
|
+
output: vals.output,
|
|
15928
|
+
total: vals.input + vals.output,
|
|
15929
|
+
cost: vals.cost,
|
|
15930
|
+
costUnit: vals.costUnit
|
|
15931
|
+
})).sort((a, b) => b.total - a.total);
|
|
15932
|
+
}
|
|
15933
|
+
});
|
|
15934
|
+
}
|
|
15935
|
+
|
|
15936
|
+
const formatHierarchicalSpans = (spans) => {
|
|
15937
|
+
if (!spans || spans.length === 0) {
|
|
15938
|
+
return [];
|
|
15939
|
+
}
|
|
15940
|
+
const overallEndDate = spans.reduce(
|
|
15941
|
+
(latest, span) => {
|
|
15942
|
+
const endDate = span?.endedAt ? new Date(span.endedAt) : void 0;
|
|
15943
|
+
return endDate && (!latest || endDate > latest) ? endDate : latest;
|
|
15944
|
+
},
|
|
15945
|
+
null
|
|
15946
|
+
);
|
|
15947
|
+
const spanMap = /* @__PURE__ */ new Map();
|
|
15948
|
+
const rootSpans = [];
|
|
15949
|
+
spans.forEach((spanRecord) => {
|
|
15950
|
+
const startDate = new Date(spanRecord.startedAt);
|
|
15951
|
+
const endDate = spanRecord.endedAt ? new Date(spanRecord.endedAt) : void 0;
|
|
15952
|
+
const uiSpan = {
|
|
15953
|
+
id: spanRecord.spanId,
|
|
15954
|
+
name: spanRecord.name,
|
|
15955
|
+
type: spanRecord.spanType,
|
|
15956
|
+
latency: endDate ? endDate.getTime() - startDate.getTime() : 0,
|
|
15957
|
+
startTime: startDate.toISOString(),
|
|
15958
|
+
endTime: endDate ? endDate.toISOString() : void 0,
|
|
15959
|
+
spans: [],
|
|
15960
|
+
parentSpanId: spanRecord.parentSpanId
|
|
15961
|
+
};
|
|
15962
|
+
spanMap.set(spanRecord.spanId, uiSpan);
|
|
15963
|
+
});
|
|
15964
|
+
spans.forEach((spanRecord) => {
|
|
15965
|
+
const uiSpan = spanMap.get(spanRecord.spanId);
|
|
15966
|
+
if (spanRecord?.parentSpanId == null) {
|
|
15967
|
+
if (overallEndDate && uiSpan.endTime && overallEndDate > new Date(uiSpan.endTime)) {
|
|
15968
|
+
uiSpan.endTime = overallEndDate.toISOString();
|
|
15969
|
+
const overallEndTime = new Date(overallEndDate).getTime();
|
|
15970
|
+
const spanStartTime = new Date(uiSpan.startTime).getTime();
|
|
15971
|
+
uiSpan.latency = overallEndTime - spanStartTime;
|
|
15972
|
+
}
|
|
15973
|
+
rootSpans.push(uiSpan);
|
|
15974
|
+
} else {
|
|
15975
|
+
const parent = spanMap.get(spanRecord.parentSpanId);
|
|
15976
|
+
if (parent) {
|
|
15977
|
+
parent.spans.push(uiSpan);
|
|
15978
|
+
} else {
|
|
15979
|
+
rootSpans.push(uiSpan);
|
|
15980
|
+
}
|
|
15981
|
+
}
|
|
15982
|
+
});
|
|
15983
|
+
const sortSpansByStartTime = (spans2) => {
|
|
15984
|
+
return spans2.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
|
|
15985
|
+
};
|
|
15986
|
+
const sortedRootSpans = sortSpansByStartTime(rootSpans);
|
|
15987
|
+
const sortNestedSpans = (spans2) => {
|
|
15988
|
+
spans2.forEach((span) => {
|
|
15989
|
+
if (span.spans && span.spans.length > 0) {
|
|
15990
|
+
span.spans = sortSpansByStartTime(span.spans);
|
|
15991
|
+
sortNestedSpans(span.spans);
|
|
15992
|
+
}
|
|
15993
|
+
});
|
|
15994
|
+
};
|
|
15995
|
+
sortNestedSpans(sortedRootSpans);
|
|
15996
|
+
return sortedRootSpans;
|
|
15997
|
+
};
|
|
15998
|
+
|
|
15999
|
+
const spanTypePrefixes = ["agent", "workflow", "model", "mcp", "tool", "memory", "workspace", "other"];
|
|
16000
|
+
const spanTypeToUiElements = {
|
|
16001
|
+
agent: {
|
|
16002
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(AgentIcon, {}),
|
|
16003
|
+
color: "oklch(0.75 0.15 250)",
|
|
16004
|
+
label: "Agent",
|
|
16005
|
+
bgColor: "oklch(0.75 0.15 250 / 0.1)",
|
|
16006
|
+
typePrefix: "agent"
|
|
16007
|
+
},
|
|
16008
|
+
workflow: {
|
|
16009
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(WorkflowIcon, {}),
|
|
16010
|
+
color: "oklch(0.75 0.15 200)",
|
|
16011
|
+
label: "Workflow",
|
|
16012
|
+
bgColor: "oklch(0.75 0.15 200 / 0.1)",
|
|
16013
|
+
typePrefix: "workflow"
|
|
16014
|
+
},
|
|
16015
|
+
model: {
|
|
16016
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BrainIcon, {}),
|
|
16017
|
+
color: "oklch(0.75 0.15 320)",
|
|
16018
|
+
label: "Model",
|
|
16019
|
+
bgColor: "oklch(0.75 0.15 320 / 0.1)",
|
|
16020
|
+
typePrefix: "model"
|
|
16021
|
+
},
|
|
16022
|
+
mcp: {
|
|
16023
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(McpServerIcon, {}),
|
|
16024
|
+
color: "oklch(0.75 0.15 160)",
|
|
16025
|
+
label: "MCP",
|
|
16026
|
+
bgColor: "oklch(0.75 0.15 160 / 0.1)",
|
|
16027
|
+
typePrefix: "mcp"
|
|
16028
|
+
},
|
|
16029
|
+
tool: {
|
|
16030
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(ToolsIcon, {}),
|
|
16031
|
+
color: "oklch(0.75 0.15 100)",
|
|
16032
|
+
label: "Tool",
|
|
16033
|
+
bgColor: "oklch(0.75 0.15 100 / 0.1)",
|
|
16034
|
+
typePrefix: "tool"
|
|
16035
|
+
},
|
|
16036
|
+
memory: {
|
|
16037
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(MemoryIcon, {}),
|
|
16038
|
+
color: "oklch(0.75 0.15 60)",
|
|
16039
|
+
label: "Memory",
|
|
16040
|
+
bgColor: "oklch(0.75 0.15 60 / 0.1)",
|
|
16041
|
+
typePrefix: "memory"
|
|
16042
|
+
},
|
|
16043
|
+
workspace: {
|
|
16044
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(FolderIcon, {}),
|
|
16045
|
+
color: "oklch(0.75 0.15 40)",
|
|
16046
|
+
label: "Workspace",
|
|
16047
|
+
bgColor: "oklch(0.75 0.15 40 / 0.1)",
|
|
16048
|
+
typePrefix: "workspace"
|
|
16049
|
+
}
|
|
16050
|
+
};
|
|
16051
|
+
const otherSpanType = {
|
|
16052
|
+
color: "oklch(0.65 0 0)",
|
|
16053
|
+
label: "Other",
|
|
16054
|
+
typePrefix: "other"
|
|
16055
|
+
};
|
|
16056
|
+
function getSpanTypeUi(type) {
|
|
16057
|
+
const typePrefix = type?.toLowerCase().split("_")[0];
|
|
16058
|
+
return spanTypeToUiElements[typePrefix] ?? otherSpanType;
|
|
16059
|
+
}
|
|
16060
|
+
|
|
16061
|
+
function isTokenDetailsObject(value) {
|
|
16062
|
+
return typeof value === "object" && value !== null;
|
|
16063
|
+
}
|
|
16064
|
+
const detailKeyLabels = {
|
|
16065
|
+
text: "Text",
|
|
16066
|
+
cacheRead: "Cache Read",
|
|
16067
|
+
cacheWrite: "Cache Write",
|
|
16068
|
+
audio: "Audio",
|
|
16069
|
+
image: "Image",
|
|
16070
|
+
reasoning: "Reasoning"
|
|
16071
|
+
};
|
|
16072
|
+
function SpanTokenUsage({ usage, className }) {
|
|
16073
|
+
if (!usage) return null;
|
|
16074
|
+
const isV5 = "inputTokens" in usage;
|
|
16075
|
+
const legacyTokenPresentations = {
|
|
16076
|
+
promptTokens: { label: "Prompt Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightIcon, {}) },
|
|
16077
|
+
completionTokens: { label: "Completion Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightToLineIcon, {}) }
|
|
16078
|
+
};
|
|
16079
|
+
const v5TokenPresentations = {
|
|
16080
|
+
inputTokens: { label: "Input Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightIcon, {}) },
|
|
16081
|
+
outputTokens: { label: "Output Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightToLineIcon, {}) },
|
|
16082
|
+
reasoningTokens: { label: "Reasoning Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightToLineIcon, {}) },
|
|
16083
|
+
cachedInputTokens: { label: "Cached Input Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightToLineIcon, {}) },
|
|
16084
|
+
inputDetails: { label: "Input Details", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightIcon, {}) },
|
|
16085
|
+
outputDetails: { label: "Output Details", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightToLineIcon, {}) }
|
|
16086
|
+
};
|
|
16087
|
+
const commonTokenPresentations = {
|
|
16088
|
+
totalTokens: { label: "Total LLM Tokens", icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CoinsIcon, {}) }
|
|
16089
|
+
};
|
|
16090
|
+
const tokenPresentations = {
|
|
16091
|
+
...commonTokenPresentations,
|
|
16092
|
+
...v5TokenPresentations,
|
|
16093
|
+
...legacyTokenPresentations
|
|
16094
|
+
};
|
|
16095
|
+
const usageKeyOrder = isV5 ? [
|
|
16096
|
+
"totalTokens",
|
|
16097
|
+
"inputTokens",
|
|
16098
|
+
"outputTokens",
|
|
16099
|
+
"reasoningTokens",
|
|
16100
|
+
"cachedInputTokens",
|
|
16101
|
+
"inputDetails",
|
|
16102
|
+
"outputDetails"
|
|
16103
|
+
] : ["totalTokens", "promptTokens", "completionTokens"];
|
|
16104
|
+
const usageAsArray = Object.entries(usage).filter((entry) => {
|
|
16105
|
+
const value = entry[1];
|
|
16106
|
+
return typeof value === "number" || isTokenDetailsObject(value);
|
|
16107
|
+
}).map(([key, value]) => ({ key, value })).sort((a, b) => usageKeyOrder.indexOf(a.key) - usageKeyOrder.indexOf(b.key));
|
|
16108
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex gap-6 flex-wrap", className), children: usageAsArray.map(({ key, value }) => {
|
|
16109
|
+
const isObject = isTokenDetailsObject(value);
|
|
16110
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-surface3 p-3 px-4 rounded-lg text-ui-md grow", children: [
|
|
16111
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16112
|
+
"div",
|
|
16113
|
+
{
|
|
16114
|
+
className: cn(
|
|
16115
|
+
"grid grid-cols-[1.5rem_1fr_auto] gap-2 items-center",
|
|
16116
|
+
"[&>svg]:w-[1.5em] [&>svg]:h-[1.5em] [&>svg]:opacity-70"
|
|
16117
|
+
),
|
|
16118
|
+
children: [
|
|
16119
|
+
tokenPresentations?.[key]?.icon,
|
|
16120
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui-md", children: tokenPresentations?.[key]?.label }),
|
|
16121
|
+
!isObject && /* @__PURE__ */ jsxRuntime.jsx("b", { className: "text-ui-lg", children: value })
|
|
16122
|
+
]
|
|
16123
|
+
}
|
|
16124
|
+
),
|
|
16125
|
+
isObject && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-ui-md mt-2 pl-8", children: Object.entries(value).map(([detailKey, detailValue]) => {
|
|
16126
|
+
if (typeof detailValue !== "number") return null;
|
|
16127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16128
|
+
"dl",
|
|
16129
|
+
{
|
|
16130
|
+
className: "grid grid-cols-[1fr_auto] gap-x-4 gap-y-1 justify-between text-neutral3",
|
|
16131
|
+
children: [
|
|
16132
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: detailKeyLabels[detailKey] || detailKey }),
|
|
16133
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: detailValue })
|
|
16134
|
+
]
|
|
16135
|
+
},
|
|
16136
|
+
detailKey
|
|
16137
|
+
);
|
|
16138
|
+
}) })
|
|
16139
|
+
] }, key);
|
|
16140
|
+
}) });
|
|
16141
|
+
}
|
|
16142
|
+
|
|
16143
|
+
function TimelineExpandCol({
|
|
16144
|
+
isSelected,
|
|
16145
|
+
isFaded,
|
|
16146
|
+
isExpanded,
|
|
16147
|
+
toggleChildren,
|
|
16148
|
+
expandAllDescendants,
|
|
16149
|
+
totalDescendants = 0,
|
|
16150
|
+
allDescendantsExpanded,
|
|
16151
|
+
numOfChildren
|
|
16152
|
+
}) {
|
|
16153
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16154
|
+
"div",
|
|
16155
|
+
{
|
|
16156
|
+
className: cn("flex items-center justify-end h-full px-1.5", {
|
|
16157
|
+
"opacity-30 [&:hover]:opacity-60": isFaded,
|
|
16158
|
+
"bg-surface4": isSelected
|
|
16159
|
+
}),
|
|
16160
|
+
children: numOfChildren && numOfChildren > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1", children: [
|
|
16161
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ExpandButton, { onClick: () => toggleChildren?.(), children: [
|
|
16162
|
+
allDescendantsExpanded ? totalDescendants : numOfChildren,
|
|
16163
|
+
" ",
|
|
16164
|
+
isExpanded ? allDescendantsExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUpIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, {})
|
|
16165
|
+
] }),
|
|
16166
|
+
totalDescendants > numOfChildren && !allDescendantsExpanded && /* @__PURE__ */ jsxRuntime.jsxs(ExpandButton, { onClick: () => expandAllDescendants?.(), children: [
|
|
16167
|
+
totalDescendants,
|
|
16168
|
+
" ",
|
|
16169
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsDownIcon, {})
|
|
16170
|
+
] })
|
|
16171
|
+
] }) : null
|
|
16172
|
+
}
|
|
16173
|
+
);
|
|
16174
|
+
}
|
|
16175
|
+
function ExpandButton({ onClick, children, className }) {
|
|
16176
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { onClick, className: cn("h-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16177
|
+
"div",
|
|
16178
|
+
{
|
|
16179
|
+
className: cn(
|
|
16180
|
+
"flex items-center gap-[0.1rem] text-ui-xs text-neutral5 border border-border1 pl-1.5 pr-0.5 rounded-md transition-all",
|
|
16181
|
+
"hover:text-yellow-500",
|
|
16182
|
+
"[&>svg]:shrink-0 [&>svg]:opacity-80 [&>svg]:w-[0.85rem] [&>svg]:h-[0.85rem] [&>svg]:transition-all"
|
|
16183
|
+
),
|
|
16184
|
+
children
|
|
16185
|
+
}
|
|
16186
|
+
) });
|
|
16187
|
+
}
|
|
16188
|
+
|
|
16189
|
+
function TimelineStructureSign({ isLastChild }) {
|
|
16190
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16191
|
+
"div",
|
|
16192
|
+
{
|
|
16193
|
+
className: cn(
|
|
16194
|
+
"w-[0.5rem] h-[1.8rem] relative opacity-100 shrink-0",
|
|
16195
|
+
'after:content-[""] after:absolute after:left-[-1px] after:top-0 after:bottom-0 after:w-[0px] after:border-l-[1px] after:border-neutral3 after:border-dashed ',
|
|
16196
|
+
'before:content-[""] before:absolute before:left-0 before:top-[50%] before:w-full before:h-[0px] before:border-b-[1px] before:border-neutral3 before:border-dashed',
|
|
16197
|
+
{
|
|
16198
|
+
"after:bottom-[50%]": isLastChild
|
|
16199
|
+
}
|
|
16200
|
+
)
|
|
16201
|
+
}
|
|
16202
|
+
);
|
|
16203
|
+
}
|
|
16204
|
+
|
|
16205
|
+
function TimelineNameCol({
|
|
16206
|
+
span,
|
|
16207
|
+
spanUI: _spanUI,
|
|
16208
|
+
isFaded,
|
|
16209
|
+
depth = 0,
|
|
16210
|
+
onSpanClick,
|
|
16211
|
+
selectedSpanId,
|
|
16212
|
+
isLastChild,
|
|
16213
|
+
hasChildren: _hasChildren,
|
|
16214
|
+
isRootSpan,
|
|
16215
|
+
isExpanded: _isExpanded
|
|
16216
|
+
}) {
|
|
16217
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16218
|
+
"div",
|
|
16219
|
+
{
|
|
16220
|
+
"data-span-id": span.id,
|
|
16221
|
+
"aria-label": `View details for span ${span.name}`,
|
|
16222
|
+
className: cn("rounded-md flex opacity-80 min-h-8 items-center rounded-l-lg", {
|
|
16223
|
+
"opacity-30 [&:hover]:opacity-60": isFaded,
|
|
16224
|
+
"bg-surface4": selectedSpanId === span.id
|
|
16225
|
+
}),
|
|
16226
|
+
style: { paddingLeft: `${depth * 1}rem` },
|
|
16227
|
+
children: [
|
|
16228
|
+
!isRootSpan && /* @__PURE__ */ jsxRuntime.jsx(TimelineStructureSign, { isLastChild }),
|
|
16229
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16230
|
+
"button",
|
|
16231
|
+
{
|
|
16232
|
+
onClick: () => onSpanClick?.(span.id),
|
|
16233
|
+
className: cn(
|
|
16234
|
+
"text-ui-sm flex items-center text-left gap-1.5 text-neutral6 w-full min-w-0 rounded-md h-full px-2 py-1 transition-colors",
|
|
16235
|
+
"[&>svg]:transition-all [&>svg]:shrink-0 [&>svg]:opacity-0 [&>svg]:w-[1em] [&>svg]:h-[1em] [&>svg]:ml-auto",
|
|
16236
|
+
"hover:bg-surface4 [&:hover>svg]:opacity-60",
|
|
16237
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-accent1"
|
|
16238
|
+
),
|
|
16239
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: span.name })
|
|
16240
|
+
}
|
|
16241
|
+
)
|
|
16242
|
+
]
|
|
16243
|
+
}
|
|
16244
|
+
);
|
|
16245
|
+
}
|
|
16246
|
+
|
|
16247
|
+
function TimelineTimingCol({
|
|
16248
|
+
span,
|
|
16249
|
+
selectedSpanId,
|
|
16250
|
+
isFaded,
|
|
16251
|
+
overallLatency,
|
|
16252
|
+
overallStartTime,
|
|
16253
|
+
color,
|
|
16254
|
+
chartWidth = "default"
|
|
16255
|
+
}) {
|
|
16256
|
+
const percentageSpanLatency = overallLatency ? Math.ceil(span.latency / overallLatency * 100) : 0;
|
|
16257
|
+
const overallStartTimeDate = overallStartTime ? new Date(overallStartTime) : null;
|
|
16258
|
+
const spanStartTimeDate = span.startTime ? new Date(span.startTime) : null;
|
|
16259
|
+
const spanStartTimeShift = spanStartTimeDate && overallStartTimeDate ? spanStartTimeDate.getTime() - overallStartTimeDate.getTime() : 0;
|
|
16260
|
+
const percentageSpanStartTime = overallLatency && Math.floor(spanStartTimeShift / overallLatency * 100);
|
|
16261
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(HoverCard__namespace.Root, { openDelay: 250, children: [
|
|
16262
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16263
|
+
HoverCard__namespace.Trigger,
|
|
16264
|
+
{
|
|
16265
|
+
className: cn(
|
|
16266
|
+
"h-8 p-1 grid grid-cols-[1fr_auto] gap-2 items-center cursor-help pr-2 rounded-r-md",
|
|
16267
|
+
chartWidth === "wide" ? "min-w-72" : "min-w-32",
|
|
16268
|
+
"[&:hover>div]:bg-surface5",
|
|
16269
|
+
{
|
|
16270
|
+
"opacity-30 [&:hover]:opacity-60": isFaded,
|
|
16271
|
+
"bg-surface4": selectedSpanId === span.id
|
|
16272
|
+
}
|
|
16273
|
+
),
|
|
16274
|
+
children: [
|
|
16275
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("w-full p-1.5 rounded-md bg-surface4 transition-colors duration-1000"), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative w-full h-1.5 rounded-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
16276
|
+
"div",
|
|
16277
|
+
{
|
|
16278
|
+
className: cn("bg-neutral1 absolute rounded-sm h-1.5 top-0"),
|
|
16279
|
+
style: {
|
|
16280
|
+
width: percentageSpanLatency ? `${percentageSpanLatency}%` : "2px",
|
|
16281
|
+
left: `${percentageSpanStartTime || 0}%`,
|
|
16282
|
+
backgroundColor: color
|
|
16283
|
+
}
|
|
16284
|
+
}
|
|
16285
|
+
) }) }),
|
|
16286
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex justify-end text-neutral3 text-ui-xs"), children: [
|
|
16287
|
+
(span.latency / 1e3).toFixed(3),
|
|
16288
|
+
" s"
|
|
16289
|
+
] })
|
|
16290
|
+
]
|
|
16291
|
+
}
|
|
16292
|
+
),
|
|
16293
|
+
/* @__PURE__ */ jsxRuntime.jsx(HoverCard__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16294
|
+
HoverCard__namespace.Content,
|
|
16295
|
+
{
|
|
16296
|
+
className: "z-50 w-auto max-w-100 rounded-md bg-surface4 p-2 px-4 pr-6 text-ui-sm text-neutral5 border border-border1",
|
|
16297
|
+
sideOffset: 5,
|
|
16298
|
+
side: "top",
|
|
16299
|
+
children: [
|
|
16300
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-ui-sm flex items-center gap-2 mb-2 mt-1"), children: "Span Timing" }),
|
|
16301
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataKeysAndValues, { children: [
|
|
16302
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Latency" }),
|
|
16303
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataKeysAndValues.Value, { children: [
|
|
16304
|
+
span.latency,
|
|
16305
|
+
" ms"
|
|
16306
|
+
] }),
|
|
16307
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Started at" }),
|
|
16308
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: span.startTime ? format.format(new Date(span.startTime), "hh:mm:ss:SSS a") : "-" }),
|
|
16309
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Ended at" }),
|
|
16310
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: span.endTime ? format.format(new Date(span.endTime), "hh:mm:ss:SSS a") : "-" }),
|
|
16311
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Start Shift" }),
|
|
16312
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataKeysAndValues.Value, { children: [
|
|
16313
|
+
spanStartTimeShift,
|
|
16314
|
+
"ms"
|
|
16315
|
+
] })
|
|
16316
|
+
] }),
|
|
16317
|
+
/* @__PURE__ */ jsxRuntime.jsx(HoverCard__namespace.Arrow, { className: "fill-surface5" })
|
|
16318
|
+
]
|
|
16319
|
+
}
|
|
16320
|
+
) })
|
|
16321
|
+
] });
|
|
16322
|
+
}
|
|
16323
|
+
|
|
16324
|
+
function computeTraceStatus(span) {
|
|
16325
|
+
if (span.error != null) return "error" /* ERROR */;
|
|
16326
|
+
if (span.endedAt == null) return "running" /* RUNNING */;
|
|
16327
|
+
return "success" /* SUCCESS */;
|
|
16328
|
+
}
|
|
16329
|
+
function TraceKeysAndValues({ rootSpan, numOfCol = 2, className }) {
|
|
16330
|
+
const startedAt = rootSpan.startedAt ? new Date(rootSpan.startedAt) : null;
|
|
16331
|
+
const endedAt = rootSpan.endedAt ? new Date(rootSpan.endedAt) : null;
|
|
16332
|
+
const status = computeTraceStatus(rootSpan);
|
|
16333
|
+
const statusLabel = status === "error" /* ERROR */ ? "ERROR" : status === "running" /* RUNNING */ ? "RUNNING" : "SUCCESS";
|
|
16334
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataKeysAndValues, { numOfCol, className, children: [
|
|
16335
|
+
rootSpan.entityId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16336
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Entity Id" }),
|
|
16337
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: rootSpan.entityName || rootSpan.entityId })
|
|
16338
|
+
] }),
|
|
16339
|
+
rootSpan.entityType && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16340
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Entity Type" }),
|
|
16341
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: rootSpan.entityType })
|
|
16342
|
+
] }),
|
|
16343
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Status" }),
|
|
16344
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: statusLabel }),
|
|
16345
|
+
startedAt && endedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16346
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Duration" }),
|
|
16347
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: `${(endedAt.getTime() - startedAt.getTime()).toLocaleString()}ms` })
|
|
16348
|
+
] }),
|
|
16349
|
+
startedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16350
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Started at" }),
|
|
16351
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: dateFns.format(startedAt, "MMM dd, h:mm:ss.SSS aaa") })
|
|
16352
|
+
] }),
|
|
16353
|
+
endedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16354
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Ended at" }),
|
|
16355
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: dateFns.format(endedAt, "MMM dd, h:mm:ss.SSS aaa") })
|
|
16356
|
+
] })
|
|
16357
|
+
] });
|
|
16358
|
+
}
|
|
16359
|
+
|
|
16360
|
+
function getSpanDescendantIds(span) {
|
|
16361
|
+
if (!span.spans || span.spans.length === 0) {
|
|
16362
|
+
return [];
|
|
16363
|
+
}
|
|
16364
|
+
const descendantIds = [];
|
|
16365
|
+
span.spans.forEach((childSpan) => {
|
|
16366
|
+
descendantIds.push(childSpan.id);
|
|
16367
|
+
descendantIds.push(...getSpanDescendantIds(childSpan));
|
|
16368
|
+
});
|
|
16369
|
+
return descendantIds;
|
|
16370
|
+
}
|
|
16371
|
+
function getAllSpanIds(spans) {
|
|
16372
|
+
const ids = [];
|
|
16373
|
+
for (const span of spans) {
|
|
16374
|
+
ids.push(span.id);
|
|
16375
|
+
ids.push(...getSpanDescendantIds(span));
|
|
16376
|
+
}
|
|
16377
|
+
return ids;
|
|
16378
|
+
}
|
|
16379
|
+
|
|
16380
|
+
function TraceTimelineSpan({
|
|
16381
|
+
span,
|
|
16382
|
+
depth = 0,
|
|
16383
|
+
onSpanClick,
|
|
16384
|
+
selectedSpanId,
|
|
16385
|
+
isLastChild,
|
|
16386
|
+
overallLatency,
|
|
16387
|
+
overallStartTime,
|
|
16388
|
+
fadedTypes,
|
|
16389
|
+
searchPhrase,
|
|
16390
|
+
featuredSpanIds,
|
|
16391
|
+
expandedSpanIds,
|
|
16392
|
+
setExpandedSpanIds,
|
|
16393
|
+
chartWidth
|
|
16394
|
+
}) {
|
|
16395
|
+
const hasChildren = span.spans && span.spans.length > 0;
|
|
16396
|
+
const numOfChildren = span.spans ? span.spans.length : 0;
|
|
16397
|
+
const allDescendantIds = getSpanDescendantIds(span);
|
|
16398
|
+
const totalDescendants = allDescendantIds.length;
|
|
16399
|
+
const isRootSpan = depth === 0;
|
|
16400
|
+
const spanUI = getSpanTypeUi(span?.type);
|
|
16401
|
+
const isExpanded = expandedSpanIds ? expandedSpanIds.includes(span.id) : false;
|
|
16402
|
+
const isFadedBySearch = featuredSpanIds && featuredSpanIds.length > 0 ? !featuredSpanIds.includes(span.id) : false;
|
|
16403
|
+
const isFadedByType = fadedTypes && fadedTypes.length > 0 ? fadedTypes.includes(spanUI?.typePrefix || "") : false;
|
|
16404
|
+
const isFaded = isFadedByType || isFadedBySearch;
|
|
16405
|
+
React.useEffect(() => {
|
|
16406
|
+
if (!featuredSpanIds || allDescendantIds.length === 0) return;
|
|
16407
|
+
if (isExpanded) return;
|
|
16408
|
+
const hasFeaturedDescendant = allDescendantIds.some((id) => featuredSpanIds.includes(id));
|
|
16409
|
+
if (hasFeaturedDescendant && setExpandedSpanIds) {
|
|
16410
|
+
setExpandedSpanIds((prev) => !prev || prev.includes(span.id) ? prev ?? [span.id] : [...prev, span.id]);
|
|
16411
|
+
}
|
|
16412
|
+
}, [featuredSpanIds, allDescendantIds]);
|
|
16413
|
+
const toggleChildren = () => {
|
|
16414
|
+
if (!setExpandedSpanIds) return;
|
|
16415
|
+
setExpandedSpanIds((prev) => {
|
|
16416
|
+
if (!prev) return prev;
|
|
16417
|
+
const idsToRemove = /* @__PURE__ */ new Set([span.id, ...allDescendantIds]);
|
|
16418
|
+
return isExpanded ? prev.filter((id) => !idsToRemove.has(id)) : [...prev, span.id];
|
|
16419
|
+
});
|
|
16420
|
+
};
|
|
16421
|
+
const expandAllDescendants = () => {
|
|
16422
|
+
if (!setExpandedSpanIds) return;
|
|
16423
|
+
setExpandedSpanIds((prev) => {
|
|
16424
|
+
if (!prev) return prev;
|
|
16425
|
+
return Array.from(/* @__PURE__ */ new Set([...prev, span.id, ...allDescendantIds]));
|
|
16426
|
+
});
|
|
16427
|
+
};
|
|
16428
|
+
const allDescendantsExpanded = allDescendantIds.every((id) => expandedSpanIds?.includes(id));
|
|
16429
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16430
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16431
|
+
TimelineNameCol,
|
|
16432
|
+
{
|
|
16433
|
+
span,
|
|
16434
|
+
spanUI,
|
|
16435
|
+
isFaded,
|
|
16436
|
+
depth,
|
|
16437
|
+
onSpanClick,
|
|
16438
|
+
selectedSpanId,
|
|
16439
|
+
isLastChild,
|
|
16440
|
+
hasChildren,
|
|
16441
|
+
isRootSpan,
|
|
16442
|
+
isExpanded
|
|
16443
|
+
}
|
|
16444
|
+
),
|
|
16445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16446
|
+
TimelineExpandCol,
|
|
16447
|
+
{
|
|
16448
|
+
isSelected: selectedSpanId === span.id,
|
|
16449
|
+
isFaded,
|
|
16450
|
+
isExpanded,
|
|
16451
|
+
toggleChildren,
|
|
16452
|
+
expandAllDescendants,
|
|
16453
|
+
totalDescendants,
|
|
16454
|
+
allDescendantsExpanded,
|
|
16455
|
+
numOfChildren
|
|
16456
|
+
}
|
|
16457
|
+
),
|
|
16458
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16459
|
+
TimelineTimingCol,
|
|
16460
|
+
{
|
|
16461
|
+
span,
|
|
16462
|
+
selectedSpanId,
|
|
16463
|
+
isFaded,
|
|
16464
|
+
overallLatency,
|
|
16465
|
+
overallStartTime,
|
|
16466
|
+
color: spanUI?.color,
|
|
16467
|
+
chartWidth
|
|
16468
|
+
}
|
|
16469
|
+
),
|
|
16470
|
+
hasChildren && isExpanded && span.spans?.map((childSpan, idx, array) => {
|
|
16471
|
+
const isLast = idx === array.length - 1;
|
|
16472
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16473
|
+
TraceTimelineSpan,
|
|
16474
|
+
{
|
|
16475
|
+
span: childSpan,
|
|
16476
|
+
depth: depth + 1,
|
|
16477
|
+
onSpanClick,
|
|
16478
|
+
selectedSpanId,
|
|
16479
|
+
isLastChild: isLast,
|
|
16480
|
+
overallLatency,
|
|
16481
|
+
overallStartTime,
|
|
16482
|
+
fadedTypes,
|
|
16483
|
+
searchPhrase,
|
|
16484
|
+
expandedSpanIds,
|
|
16485
|
+
setExpandedSpanIds,
|
|
16486
|
+
featuredSpanIds,
|
|
16487
|
+
chartWidth
|
|
16488
|
+
},
|
|
16489
|
+
childSpan.id
|
|
16490
|
+
);
|
|
16491
|
+
})
|
|
16492
|
+
] });
|
|
16493
|
+
}
|
|
16494
|
+
|
|
16495
|
+
function TraceTimeline({
|
|
16496
|
+
hierarchicalSpans = [],
|
|
16497
|
+
onSpanClick,
|
|
16498
|
+
selectedSpanId,
|
|
16499
|
+
isLoading,
|
|
16500
|
+
fadedTypes,
|
|
16501
|
+
expandedSpanIds,
|
|
16502
|
+
setExpandedSpanIds,
|
|
16503
|
+
featuredSpanIds,
|
|
16504
|
+
chartWidth = "default"
|
|
16505
|
+
}) {
|
|
16506
|
+
const overallLatency = hierarchicalSpans?.[0]?.latency || 0;
|
|
16507
|
+
const overallStartTime = hierarchicalSpans?.[0]?.startTime || "";
|
|
16508
|
+
const usedSpanTypes = React.useMemo(() => {
|
|
16509
|
+
const collectTypes = (spans) => {
|
|
16510
|
+
const types2 = /* @__PURE__ */ new Set();
|
|
16511
|
+
for (const span of spans) {
|
|
16512
|
+
const prefix = span.type?.toLowerCase().split("_")[0];
|
|
16513
|
+
if (prefix) types2.add(prefix);
|
|
16514
|
+
if (span.spans) {
|
|
16515
|
+
for (const t of collectTypes(span.spans)) types2.add(t);
|
|
16516
|
+
}
|
|
16517
|
+
}
|
|
16518
|
+
return types2;
|
|
16519
|
+
};
|
|
16520
|
+
const types = collectTypes(hierarchicalSpans);
|
|
16521
|
+
const hasOther = [...types].some((t) => !spanTypePrefixes.includes(t));
|
|
16522
|
+
const known = spanTypePrefixes.filter((p) => p !== "other" && types.has(p));
|
|
16523
|
+
if (hasOther) known.push("other");
|
|
16524
|
+
return known;
|
|
16525
|
+
}, [hierarchicalSpans]);
|
|
16526
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16527
|
+
"div",
|
|
16528
|
+
{
|
|
16529
|
+
className: cn(
|
|
16530
|
+
"flex items-center text-ui-sm gap-3 bg-surface3/50 rounded-md p-3 justify-center text-neutral3",
|
|
16531
|
+
"[&_svg]:w-[1.25em] [&_svg]:h-[1.25em] [&_svg]:opacity-50"
|
|
16532
|
+
),
|
|
16533
|
+
children: [
|
|
16534
|
+
/* @__PURE__ */ jsxRuntime.jsx(Spinner, {}),
|
|
16535
|
+
" Loading Trace Timeline ..."
|
|
16536
|
+
]
|
|
16537
|
+
}
|
|
16538
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16539
|
+
usedSpanTypes.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3 px-2 py-1.5 justify-end", children: usedSpanTypes.map((type) => {
|
|
16540
|
+
const spanUI = getSpanTypeUi(type);
|
|
16541
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-ui-sm text-neutral3", children: [
|
|
16542
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16543
|
+
"span",
|
|
16544
|
+
{
|
|
16545
|
+
className: "inline-block w-1.5 h-1.5 rounded-full shrink-0",
|
|
16546
|
+
style: { backgroundColor: spanUI?.color }
|
|
16547
|
+
}
|
|
16548
|
+
),
|
|
16549
|
+
spanUI?.label || type
|
|
16550
|
+
] }, type);
|
|
16551
|
+
}) }),
|
|
16552
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-[minmax(0,1fr)_auto_auto] items-start content-start gap-y-px overflow-hidden py-1", children: hierarchicalSpans?.map((span) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
16553
|
+
TraceTimelineSpan,
|
|
16554
|
+
{
|
|
16555
|
+
span,
|
|
16556
|
+
onSpanClick,
|
|
16557
|
+
selectedSpanId,
|
|
16558
|
+
overallLatency,
|
|
16559
|
+
overallStartTime,
|
|
16560
|
+
fadedTypes,
|
|
16561
|
+
featuredSpanIds,
|
|
16562
|
+
expandedSpanIds,
|
|
16563
|
+
setExpandedSpanIds,
|
|
16564
|
+
chartWidth
|
|
16565
|
+
},
|
|
16566
|
+
span.id
|
|
16567
|
+
)) })
|
|
16568
|
+
] }) });
|
|
16569
|
+
}
|
|
16570
|
+
|
|
16571
|
+
function TracesToolbar({
|
|
16572
|
+
onClear,
|
|
16573
|
+
onRemoveAll,
|
|
16574
|
+
onSave,
|
|
16575
|
+
onRemoveSaved,
|
|
16576
|
+
isLoading,
|
|
16577
|
+
filterFields,
|
|
16578
|
+
filterTokens,
|
|
16579
|
+
onFilterTokensChange,
|
|
16580
|
+
autoFocusFilterFieldId
|
|
16581
|
+
}) {
|
|
16582
|
+
const hasActiveFilters = filterTokens.length > 0;
|
|
16583
|
+
const hasNonDefaultFilter = filterTokens.some((token) => isNonDefaultFilter$1(token, filterFields));
|
|
16584
|
+
return (
|
|
16585
|
+
// 1fr | auto — pills wrap in the first column; Clear stays pinned to the
|
|
16586
|
+
// top of the second column regardless of how many pill rows render.
|
|
16587
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid grid-cols-[1fr_auto] gap-3 items-start "), children: [
|
|
16588
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16589
|
+
PropertyFilterApplied,
|
|
16590
|
+
{
|
|
16591
|
+
fields: filterFields,
|
|
16592
|
+
tokens: filterTokens,
|
|
16593
|
+
onTokensChange: onFilterTokensChange,
|
|
16594
|
+
disabled: isLoading,
|
|
16595
|
+
autoFocusFieldId: autoFocusFilterFieldId
|
|
16596
|
+
}
|
|
16597
|
+
),
|
|
16598
|
+
hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
|
|
16599
|
+
PropertyFilterActions,
|
|
16600
|
+
{
|
|
16601
|
+
disabled: isLoading,
|
|
16602
|
+
onClear: hasNonDefaultFilter ? onClear : void 0,
|
|
16603
|
+
onRemoveAll,
|
|
16604
|
+
onSave,
|
|
16605
|
+
onRemoveSaved
|
|
16606
|
+
}
|
|
16607
|
+
)
|
|
16608
|
+
] })
|
|
16609
|
+
);
|
|
16610
|
+
}
|
|
16611
|
+
function isNonDefaultFilter$1(token, fields) {
|
|
16612
|
+
const field = fields.find((f) => f.id === token.fieldId);
|
|
16613
|
+
if (!field) return false;
|
|
16614
|
+
if (field.kind === "text") {
|
|
16615
|
+
return typeof token.value === "string" && token.value.trim() !== "";
|
|
16616
|
+
}
|
|
16617
|
+
if (field.kind === "pick-multi") {
|
|
16618
|
+
if (field.multi) return Array.isArray(token.value) && token.value.length > 0;
|
|
16619
|
+
return typeof token.value === "string" && token.value !== "" && token.value !== "Any";
|
|
16620
|
+
}
|
|
16621
|
+
if (field.kind === "multi-select") {
|
|
16622
|
+
return Array.isArray(token.value) && token.value.length > 0;
|
|
16623
|
+
}
|
|
16624
|
+
return false;
|
|
16625
|
+
}
|
|
16626
|
+
|
|
16627
|
+
function TraceDataPanelView({
|
|
16628
|
+
traceId,
|
|
16629
|
+
spans,
|
|
16630
|
+
isLoading,
|
|
16631
|
+
onClose,
|
|
16632
|
+
onSpanSelect,
|
|
16633
|
+
onEvaluateTrace,
|
|
16634
|
+
onSaveAsDatasetItem,
|
|
16635
|
+
initialSpanId,
|
|
16636
|
+
onPrevious,
|
|
16637
|
+
onNext,
|
|
16638
|
+
collapsed: controlledCollapsed,
|
|
16639
|
+
onCollapsedChange,
|
|
16640
|
+
placement,
|
|
16641
|
+
timelineChartWidth = "default",
|
|
16642
|
+
LinkComponent,
|
|
16643
|
+
traceHref
|
|
16644
|
+
}) {
|
|
16645
|
+
const isOnTracePage = placement === "trace-page";
|
|
16646
|
+
const [internalCollapsed, setInternalCollapsed] = React.useState(false);
|
|
16647
|
+
const collapsed = controlledCollapsed ?? internalCollapsed;
|
|
16648
|
+
const setCollapsed = onCollapsedChange ?? setInternalCollapsed;
|
|
16649
|
+
const contentRef = React.useRef(null);
|
|
16650
|
+
const [selectedSpanId, setSelectedSpanId] = React.useState(initialSpanId ?? void 0);
|
|
16651
|
+
React.useEffect(() => {
|
|
16652
|
+
if (!initialSpanId) {
|
|
16653
|
+
setSelectedSpanId(void 0);
|
|
16654
|
+
onSpanSelect?.(void 0);
|
|
16655
|
+
return;
|
|
16656
|
+
}
|
|
16657
|
+
if (!spans) return;
|
|
16658
|
+
const found = spans.find((s) => s.spanId === initialSpanId);
|
|
16659
|
+
if (found) {
|
|
16660
|
+
setSelectedSpanId(initialSpanId);
|
|
16661
|
+
onSpanSelect?.(initialSpanId);
|
|
16662
|
+
} else {
|
|
16663
|
+
setSelectedSpanId(void 0);
|
|
16664
|
+
onSpanSelect?.(void 0);
|
|
16665
|
+
}
|
|
16666
|
+
}, [initialSpanId, spans]);
|
|
16667
|
+
React.useEffect(() => {
|
|
16668
|
+
if (!selectedSpanId || !contentRef.current) return;
|
|
16669
|
+
const el = contentRef.current.querySelector(`[data-span-id="${selectedSpanId}"]`);
|
|
16670
|
+
el?.scrollIntoView({ block: "nearest" });
|
|
16671
|
+
}, [selectedSpanId]);
|
|
16672
|
+
const hierarchicalSpans = React.useMemo(() => formatHierarchicalSpans(spans ?? []), [spans]);
|
|
16673
|
+
const [expandedSpanIds, setExpandedSpanIds] = React.useState([]);
|
|
16674
|
+
React.useEffect(() => {
|
|
16675
|
+
if (hierarchicalSpans.length > 0) {
|
|
16676
|
+
setExpandedSpanIds(getAllSpanIds(hierarchicalSpans));
|
|
16677
|
+
}
|
|
16678
|
+
}, [hierarchicalSpans]);
|
|
16679
|
+
const rootSpan = React.useMemo(() => spans?.find((s) => s.parentSpanId == null), [spans]);
|
|
16680
|
+
const handleSpanClick = (id) => {
|
|
16681
|
+
const newId = selectedSpanId === id ? void 0 : id;
|
|
16682
|
+
setSelectedSpanId(newId);
|
|
16683
|
+
onSpanSelect?.(newId);
|
|
16684
|
+
};
|
|
16685
|
+
const showOpenTracePageLink = !isOnTracePage && LinkComponent && traceHref;
|
|
16686
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataPanel, { collapsed, children: [
|
|
16687
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataPanel.Header, { children: isOnTracePage ? /* @__PURE__ */ jsxRuntime.jsx(DataPanel.Heading, { children: "Trace Timeline" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16688
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataPanel.Heading, { children: [
|
|
16689
|
+
"Trace ",
|
|
16690
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
16691
|
+
"# ",
|
|
16692
|
+
truncateString(traceId, 12)
|
|
16693
|
+
] })
|
|
16694
|
+
] }),
|
|
16695
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { className: "ml-auto shrink-0", children: [
|
|
16696
|
+
onCollapsedChange && /* @__PURE__ */ jsxRuntime.jsx(
|
|
16697
|
+
ButtonWithTooltip,
|
|
16698
|
+
{
|
|
16699
|
+
size: "md",
|
|
16700
|
+
tooltipContent: collapsed ? "Expand panel" : "Collapse panel",
|
|
16701
|
+
onClick: () => setCollapsed(!collapsed),
|
|
16702
|
+
children: collapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsDownUpIcon, {})
|
|
16703
|
+
}
|
|
16704
|
+
),
|
|
16705
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16706
|
+
DataPanel.NextPrevNav,
|
|
16707
|
+
{
|
|
16708
|
+
onPrevious,
|
|
16709
|
+
onNext,
|
|
16710
|
+
previousLabel: "Previous trace",
|
|
16711
|
+
nextLabel: "Next trace"
|
|
16712
|
+
}
|
|
16713
|
+
),
|
|
16714
|
+
showOpenTracePageLink && /* @__PURE__ */ jsxRuntime.jsx(
|
|
16715
|
+
ButtonWithTooltip,
|
|
16716
|
+
{
|
|
16717
|
+
as: LinkComponent,
|
|
16718
|
+
href: traceHref,
|
|
16719
|
+
size: "md",
|
|
16720
|
+
tooltipContent: "Open trace page",
|
|
16721
|
+
"aria-label": "Open trace page",
|
|
16722
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Link2Icon, {})
|
|
16723
|
+
}
|
|
16724
|
+
),
|
|
16725
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataPanel.CloseButton, { onClick: onClose })
|
|
16726
|
+
] })
|
|
16727
|
+
] }) }),
|
|
16728
|
+
!collapsed && (isLoading ? /* @__PURE__ */ jsxRuntime.jsx(DataPanel.LoadingData, { children: "Loading trace..." }) : hierarchicalSpans.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(DataPanel.NoData, { children: "No spans found for this trace." }) : /* @__PURE__ */ jsxRuntime.jsxs(DataPanel.Content, { ref: contentRef, children: [
|
|
16729
|
+
!isOnTracePage && rootSpan && /* @__PURE__ */ jsxRuntime.jsx(TraceKeysAndValues, { rootSpan, className: "mb-6" }),
|
|
16730
|
+
!isOnTracePage && (onEvaluateTrace || onSaveAsDatasetItem) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 flex justify-between items-center gap-4", children: [
|
|
16731
|
+
onEvaluateTrace && /* @__PURE__ */ jsxRuntime.jsxs(Button, { size: "sm", onClick: onEvaluateTrace, children: [
|
|
16732
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleGaugeIcon, {}) }),
|
|
16733
|
+
"Evaluate Trace"
|
|
16734
|
+
] }),
|
|
16735
|
+
onSaveAsDatasetItem && /* @__PURE__ */ jsxRuntime.jsxs(Button, { size: "sm", onClick: () => onSaveAsDatasetItem({ traceId, rootSpanId: rootSpan?.spanId }), children: [
|
|
16736
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SaveIcon, {}) }),
|
|
16737
|
+
"Save as Dataset Item"
|
|
16738
|
+
] })
|
|
16739
|
+
] }),
|
|
16740
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16741
|
+
TraceTimeline,
|
|
16742
|
+
{
|
|
16743
|
+
hierarchicalSpans,
|
|
16744
|
+
onSpanClick: handleSpanClick,
|
|
16745
|
+
selectedSpanId,
|
|
16746
|
+
expandedSpanIds,
|
|
16747
|
+
setExpandedSpanIds,
|
|
16748
|
+
chartWidth: timelineChartWidth
|
|
16749
|
+
}
|
|
16750
|
+
)
|
|
16751
|
+
] }))
|
|
16752
|
+
] });
|
|
16753
|
+
}
|
|
16754
|
+
|
|
16755
|
+
function getInputPreview(input, maxLength = 100) {
|
|
16756
|
+
if (input == null) return "";
|
|
16757
|
+
const messageArray = Array.isArray(input) ? input : input && typeof input === "object" && !Array.isArray(input) && Array.isArray(input.messages) ? input.messages : null;
|
|
16758
|
+
if (messageArray) {
|
|
16759
|
+
const messages = messageArray;
|
|
16760
|
+
const userMessages = messages.filter((m) => m?.role === "user").map((m) => {
|
|
16761
|
+
if (typeof m.content === "string") return m.content;
|
|
16762
|
+
if (Array.isArray(m.content)) {
|
|
16763
|
+
return m.content.map((part) => {
|
|
16764
|
+
if (typeof part === "string") return part;
|
|
16765
|
+
if (part?.type === "text" && typeof part.text === "string") return part.text;
|
|
16766
|
+
return "";
|
|
16767
|
+
}).filter(Boolean).join(" ");
|
|
16768
|
+
}
|
|
16769
|
+
return "";
|
|
16770
|
+
}).filter(Boolean);
|
|
16771
|
+
const joined = userMessages.join(" | ");
|
|
16772
|
+
if (joined.length > maxLength) {
|
|
16773
|
+
return joined.slice(0, maxLength) + "…";
|
|
16774
|
+
}
|
|
16775
|
+
return joined;
|
|
16776
|
+
}
|
|
16777
|
+
if (typeof input === "string") {
|
|
16778
|
+
if (input.length > maxLength) {
|
|
16779
|
+
return input.slice(0, maxLength) + "…";
|
|
16780
|
+
}
|
|
16781
|
+
return input;
|
|
16782
|
+
}
|
|
16783
|
+
const str = JSON.stringify(input);
|
|
16784
|
+
if (str.length > maxLength) {
|
|
16785
|
+
return str.slice(0, maxLength) + "…";
|
|
16786
|
+
}
|
|
16787
|
+
return str;
|
|
16788
|
+
}
|
|
16789
|
+
function isTokenLimitExceeded(span) {
|
|
16790
|
+
return span?.attributes?.finishReason === "length";
|
|
16791
|
+
}
|
|
16792
|
+
function getTokenLimitMessage(span) {
|
|
16793
|
+
const usage = span?.attributes?.usage;
|
|
16794
|
+
if (!usage) {
|
|
16795
|
+
return `The model stopped generating because it reached the maximum token limit. The response was truncated and may be incomplete.`;
|
|
16796
|
+
}
|
|
16797
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
16798
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
16799
|
+
const totalTokens = usage.totalTokens ?? inputTokens + outputTokens;
|
|
16800
|
+
if (inputTokens > 0 || outputTokens > 0) {
|
|
16801
|
+
return `The model stopped generating because it reached the maximum token limit. The response was truncated and may be incomplete.
|
|
16802
|
+
|
|
16803
|
+
Token usage: ${inputTokens} input + ${outputTokens} output = ${totalTokens} total`;
|
|
16804
|
+
}
|
|
16805
|
+
return `The model stopped generating because it reached the maximum token limit (${totalTokens} tokens). The response was truncated and may be incomplete.`;
|
|
16806
|
+
}
|
|
16807
|
+
|
|
16808
|
+
function buildDialogTitle(sectionTitle, icon, span) {
|
|
16809
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16810
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5 text-neutral2 uppercase tracking-widest [&>svg]:size-3.5", children: [
|
|
16811
|
+
icon,
|
|
16812
|
+
sectionTitle
|
|
16813
|
+
] }),
|
|
16814
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
16815
|
+
"› Span ",
|
|
16816
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { className: "text-neutral3", children: [
|
|
16817
|
+
"#",
|
|
16818
|
+
span.spanId
|
|
16819
|
+
] })
|
|
16820
|
+
] }),
|
|
16821
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
16822
|
+
"› Trace ",
|
|
16823
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { className: "text-neutral3", children: [
|
|
16824
|
+
"#",
|
|
16825
|
+
span.traceId
|
|
16826
|
+
] })
|
|
16827
|
+
] })
|
|
16828
|
+
] });
|
|
16829
|
+
}
|
|
16830
|
+
function SpanDataPanelView({
|
|
16831
|
+
traceId,
|
|
16832
|
+
spanId,
|
|
16833
|
+
span,
|
|
16834
|
+
isLoading,
|
|
16835
|
+
onClose,
|
|
16836
|
+
onPrevious,
|
|
16837
|
+
onNext,
|
|
16838
|
+
activeTab,
|
|
16839
|
+
onTabChange,
|
|
16840
|
+
scoringTabSlot,
|
|
16841
|
+
scoringTabBadge
|
|
16842
|
+
}) {
|
|
16843
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataPanel, { children: [
|
|
16844
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataPanel.Header, { children: [
|
|
16845
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataPanel.Heading, { children: [
|
|
16846
|
+
"Span ",
|
|
16847
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
16848
|
+
"# ",
|
|
16849
|
+
spanId
|
|
16850
|
+
] })
|
|
16851
|
+
] }),
|
|
16852
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { className: "ml-auto shrink-0", children: [
|
|
16853
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16854
|
+
DataPanel.NextPrevNav,
|
|
16855
|
+
{
|
|
16856
|
+
onPrevious,
|
|
16857
|
+
onNext,
|
|
16858
|
+
previousLabel: "Previous span",
|
|
16859
|
+
nextLabel: "Next span"
|
|
16860
|
+
}
|
|
16861
|
+
),
|
|
16862
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataPanel.CloseButton, { onClick: onClose })
|
|
16863
|
+
] })
|
|
16864
|
+
] }),
|
|
16865
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(DataPanel.LoadingData, { children: "Loading span details..." }) : !span ? /* @__PURE__ */ jsxRuntime.jsx(DataPanel.NoData, { children: "Span not found." }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
16866
|
+
SpanDataPanelContent,
|
|
16867
|
+
{
|
|
16868
|
+
span,
|
|
16869
|
+
traceId,
|
|
16870
|
+
spanId,
|
|
16871
|
+
activeTab,
|
|
16872
|
+
onTabChange,
|
|
16873
|
+
scoringTabSlot,
|
|
16874
|
+
scoringTabBadge
|
|
16875
|
+
}
|
|
16876
|
+
)
|
|
16877
|
+
] });
|
|
16878
|
+
}
|
|
16879
|
+
function SpanDataPanelContent({
|
|
16880
|
+
span,
|
|
16881
|
+
traceId,
|
|
16882
|
+
spanId,
|
|
16883
|
+
activeTab,
|
|
16884
|
+
onTabChange,
|
|
16885
|
+
scoringTabSlot,
|
|
16886
|
+
scoringTabBadge
|
|
16887
|
+
}) {
|
|
16888
|
+
const durationMs = span.startedAt && span.endedAt ? new Date(span.endedAt).getTime() - new Date(span.startedAt).getTime() : null;
|
|
16889
|
+
const usage = span.attributes?.usage;
|
|
16890
|
+
const detailsBody = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16891
|
+
isTokenLimitExceeded(span) && /* @__PURE__ */ jsxRuntime.jsxs(Alert, { variant: "warning", className: "mb-3", children: [
|
|
16892
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertTitle, { children: "Token Limit Exceeded" }),
|
|
16893
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDescription, { as: "p", children: getTokenLimitMessage(span) })
|
|
16894
|
+
] }),
|
|
16895
|
+
usage && /* @__PURE__ */ jsxRuntime.jsx(SpanTokenUsage, { usage, className: "mb-3" }),
|
|
16896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataKeysAndValues, { children: [
|
|
16897
|
+
span.parentSpanId == null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16898
|
+
span.traceId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16899
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Trace Id" }),
|
|
16900
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.ValueWithCopyBtn, { copyTooltip: "Copy Trace Id to clipboard", copyValue: span.traceId, children: span.traceId })
|
|
16901
|
+
] }),
|
|
16902
|
+
span.tags && span.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16903
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Tags" }),
|
|
16904
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: span.tags.join(", ") })
|
|
16905
|
+
] }),
|
|
16906
|
+
span.runId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16907
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Run Id" }),
|
|
16908
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.ValueWithCopyBtn, { copyTooltip: "Copy Run Id to clipboard", copyValue: span.runId, children: span.runId })
|
|
16909
|
+
] }),
|
|
16910
|
+
span.threadId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16911
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Thread Id" }),
|
|
16912
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.ValueWithCopyBtn, { copyTooltip: "Copy Thread Id to clipboard", copyValue: span.threadId, children: span.threadId })
|
|
16913
|
+
] }),
|
|
16914
|
+
span.sessionId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16915
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Session Id" }),
|
|
16916
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16917
|
+
DataKeysAndValues.ValueWithCopyBtn,
|
|
16918
|
+
{
|
|
16919
|
+
copyTooltip: "Copy Session Id to clipboard",
|
|
16920
|
+
copyValue: span.sessionId,
|
|
16921
|
+
children: span.sessionId
|
|
16922
|
+
}
|
|
16923
|
+
)
|
|
16924
|
+
] }),
|
|
16925
|
+
span.requestId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16926
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Request Id" }),
|
|
16927
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16928
|
+
DataKeysAndValues.ValueWithCopyBtn,
|
|
16929
|
+
{
|
|
16930
|
+
copyTooltip: "Copy Request Id to clipboard",
|
|
16931
|
+
copyValue: span.requestId,
|
|
16932
|
+
children: span.requestId
|
|
16933
|
+
}
|
|
16934
|
+
)
|
|
16935
|
+
] }),
|
|
16936
|
+
span.resourceId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16937
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Resource Id" }),
|
|
16938
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16939
|
+
DataKeysAndValues.ValueWithCopyBtn,
|
|
16940
|
+
{
|
|
16941
|
+
copyTooltip: "Copy Resource Id to clipboard",
|
|
16942
|
+
copyValue: span.resourceId,
|
|
16943
|
+
children: span.resourceId
|
|
16944
|
+
}
|
|
16945
|
+
)
|
|
16946
|
+
] }),
|
|
16947
|
+
span.userId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16948
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "User Id" }),
|
|
16949
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.ValueWithCopyBtn, { copyTooltip: "Copy User Id to clipboard", copyValue: span.userId, children: span.userId })
|
|
16950
|
+
] }),
|
|
16951
|
+
span.organizationId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16952
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Organization Id" }),
|
|
16953
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16954
|
+
DataKeysAndValues.ValueWithCopyBtn,
|
|
16955
|
+
{
|
|
16956
|
+
copyTooltip: "Copy Organization Id to clipboard",
|
|
16957
|
+
copyValue: span.organizationId,
|
|
16958
|
+
children: span.organizationId
|
|
16959
|
+
}
|
|
16960
|
+
)
|
|
16961
|
+
] }),
|
|
16962
|
+
span.experimentId && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16963
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Experiment Id" }),
|
|
16964
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16965
|
+
DataKeysAndValues.ValueWithCopyBtn,
|
|
16966
|
+
{
|
|
16967
|
+
copyTooltip: "Copy Experiment Id to clipboard",
|
|
16968
|
+
copyValue: span.experimentId,
|
|
16969
|
+
children: span.experimentId
|
|
16970
|
+
}
|
|
16971
|
+
)
|
|
16972
|
+
] })
|
|
16973
|
+
] }),
|
|
16974
|
+
span.name && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16975
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Name" }),
|
|
16976
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: span.name })
|
|
16977
|
+
] }),
|
|
16978
|
+
span.spanType && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16979
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Type" }),
|
|
16980
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: span.spanType })
|
|
16981
|
+
] }),
|
|
16982
|
+
span.startedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16983
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Started" }),
|
|
16984
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: dateFns.format(new Date(span.startedAt), "MMM dd, HH:mm:ss.SSS") })
|
|
16985
|
+
] }),
|
|
16986
|
+
span.endedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16987
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Ended" }),
|
|
16988
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: dateFns.format(new Date(span.endedAt), "MMM dd, HH:mm:ss.SSS") })
|
|
16989
|
+
] }),
|
|
16990
|
+
durationMs != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
16991
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Key, { children: "Duration" }),
|
|
16992
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataKeysAndValues.Value, { children: durationMs < 1e3 ? `${durationMs}ms` : `${(durationMs / 1e3).toFixed(2)}s` })
|
|
16993
|
+
] })
|
|
16994
|
+
] }),
|
|
16995
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-3 mt-3", children: [
|
|
16996
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16997
|
+
DataPanel.CodeSection,
|
|
16998
|
+
{
|
|
16999
|
+
title: "Input",
|
|
17000
|
+
dialogTitle: buildDialogTitle("Input", /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileInputIcon, {}), { spanId, traceId }),
|
|
17001
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileInputIcon, {}),
|
|
17002
|
+
codeStr: JSON.stringify(span.input ?? null, null, 2)
|
|
17003
|
+
}
|
|
17004
|
+
),
|
|
17005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17006
|
+
DataPanel.CodeSection,
|
|
17007
|
+
{
|
|
17008
|
+
title: "Output",
|
|
17009
|
+
dialogTitle: buildDialogTitle("Output", /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileOutputIcon, {}), { spanId, traceId }),
|
|
17010
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileOutputIcon, {}),
|
|
17011
|
+
codeStr: JSON.stringify(span.output ?? null, null, 2)
|
|
17012
|
+
}
|
|
17013
|
+
),
|
|
17014
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17015
|
+
DataPanel.CodeSection,
|
|
17016
|
+
{
|
|
17017
|
+
title: "Metadata",
|
|
17018
|
+
dialogTitle: buildDialogTitle("Metadata", /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}), { spanId, traceId }),
|
|
17019
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}),
|
|
17020
|
+
codeStr: JSON.stringify(span.metadata ?? null, null, 2)
|
|
17021
|
+
}
|
|
17022
|
+
),
|
|
17023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17024
|
+
DataPanel.CodeSection,
|
|
17025
|
+
{
|
|
17026
|
+
title: "Attributes",
|
|
17027
|
+
dialogTitle: buildDialogTitle("Attributes", /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}), { spanId, traceId }),
|
|
17028
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}),
|
|
17029
|
+
codeStr: JSON.stringify(span.attributes ?? null, null, 2)
|
|
17030
|
+
}
|
|
17031
|
+
)
|
|
17032
|
+
] })
|
|
17033
|
+
] });
|
|
17034
|
+
if (!scoringTabSlot) {
|
|
17035
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataPanel.Content, { children: detailsBody });
|
|
17036
|
+
}
|
|
17037
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataPanel.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tabs, { defaultTab: "details", value: activeTab, onValueChange: onTabChange, children: [
|
|
17038
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TabList, { children: [
|
|
17039
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tab, { value: "details", children: "Details" }),
|
|
17040
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Tab, { value: "scoring", children: [
|
|
17041
|
+
"Scoring ",
|
|
17042
|
+
scoringTabBadge != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17043
|
+
"(",
|
|
17044
|
+
scoringTabBadge,
|
|
17045
|
+
")"
|
|
17046
|
+
] })
|
|
17047
|
+
] })
|
|
17048
|
+
] }),
|
|
17049
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "details", children: detailsBody }),
|
|
17050
|
+
/* @__PURE__ */ jsxRuntime.jsx(TabContent, { value: "scoring", children: scoringTabSlot({ span, traceId, spanId }) })
|
|
17051
|
+
] }) });
|
|
17052
|
+
}
|
|
17053
|
+
|
|
17054
|
+
const KV$1 = DataDetailsPanel.KeyValueList;
|
|
17055
|
+
function SpanDetailsView({ spanId, span, isLoading, onClose }) {
|
|
17056
|
+
const durationMs = span?.startedAt && span?.endedAt ? new Date(span.endedAt).getTime() - new Date(span.startedAt).getTime() : null;
|
|
17057
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel, { children: [
|
|
17058
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Header, { children: [
|
|
17059
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Heading, { children: [
|
|
17060
|
+
"Span ",
|
|
17061
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
17062
|
+
"# ",
|
|
17063
|
+
spanId
|
|
17064
|
+
] })
|
|
17065
|
+
] }),
|
|
17066
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.CloseButton, { onClick: onClose })
|
|
17067
|
+
] }),
|
|
17068
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.LoadingData, { children: "Loading span..." }) : !span ? /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.NoData, { children: "Span not found." }) : /* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Content, { children: [
|
|
17069
|
+
/* @__PURE__ */ jsxRuntime.jsxs(KV$1, { children: [
|
|
17070
|
+
span.spanType && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17071
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Key, { children: "Type" }),
|
|
17072
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Value, { children: span.spanType })
|
|
17073
|
+
] }),
|
|
17074
|
+
span.startedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17075
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Key, { children: "Started" }),
|
|
17076
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Value, { children: dateFns.format(new Date(span.startedAt), "MMM dd, HH:mm:ss.SSS") })
|
|
17077
|
+
] }),
|
|
17078
|
+
span.endedAt && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17079
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Key, { children: "Ended" }),
|
|
17080
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Value, { children: dateFns.format(new Date(span.endedAt), "MMM dd, HH:mm:ss.SSS") })
|
|
17081
|
+
] }),
|
|
17082
|
+
durationMs != null && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17083
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Key, { children: "Duration" }),
|
|
17084
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV$1.Value, { children: durationMs < 1e3 ? `${durationMs}ms` : `${(durationMs / 1e3).toFixed(2)}s` })
|
|
17085
|
+
] })
|
|
17086
|
+
] }),
|
|
17087
|
+
/* @__PURE__ */ jsxRuntime.jsx("br", {}),
|
|
17088
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17089
|
+
DataDetailsPanel.CodeSection,
|
|
17090
|
+
{
|
|
17091
|
+
title: "Input",
|
|
17092
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileInputIcon, {}),
|
|
17093
|
+
codeStr: JSON.stringify(span.input ?? null, null, 2)
|
|
17094
|
+
}
|
|
17095
|
+
),
|
|
17096
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17097
|
+
DataDetailsPanel.CodeSection,
|
|
17098
|
+
{
|
|
17099
|
+
title: "Output",
|
|
17100
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileOutputIcon, {}),
|
|
17101
|
+
codeStr: JSON.stringify(span.output ?? null, null, 2)
|
|
17102
|
+
}
|
|
17103
|
+
),
|
|
17104
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17105
|
+
DataDetailsPanel.CodeSection,
|
|
17106
|
+
{
|
|
17107
|
+
title: "Metadata",
|
|
17108
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}),
|
|
17109
|
+
codeStr: JSON.stringify(span.metadata ?? null, null, 2)
|
|
17110
|
+
}
|
|
17111
|
+
),
|
|
17112
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17113
|
+
DataDetailsPanel.CodeSection,
|
|
17114
|
+
{
|
|
17115
|
+
title: "Attributes",
|
|
17116
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BracesIcon, {}),
|
|
17117
|
+
codeStr: JSON.stringify(span.attributes ?? null, null, 2)
|
|
17118
|
+
}
|
|
17119
|
+
)
|
|
17120
|
+
] })
|
|
17121
|
+
] });
|
|
17122
|
+
}
|
|
17123
|
+
|
|
17124
|
+
function TraceDetailsView({
|
|
17125
|
+
traceId,
|
|
17126
|
+
spans,
|
|
17127
|
+
isLoading,
|
|
17128
|
+
onClose,
|
|
17129
|
+
onSpanSelect,
|
|
17130
|
+
selectedSpanId
|
|
17131
|
+
}) {
|
|
17132
|
+
const hierarchicalSpans = React.useMemo(() => formatHierarchicalSpans(spans ?? []), [spans]);
|
|
17133
|
+
const [expandedSpanIds, setExpandedSpanIds] = React.useState([]);
|
|
17134
|
+
React.useEffect(() => {
|
|
17135
|
+
if (hierarchicalSpans.length > 0) {
|
|
17136
|
+
setExpandedSpanIds(getAllSpanIds(hierarchicalSpans));
|
|
17137
|
+
}
|
|
17138
|
+
}, [hierarchicalSpans]);
|
|
17139
|
+
const handleSpanClick = (id) => {
|
|
17140
|
+
onSpanSelect?.(selectedSpanId === id ? void 0 : id);
|
|
17141
|
+
};
|
|
17142
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel, { children: [
|
|
17143
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Header, { children: [
|
|
17144
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Heading, { children: [
|
|
17145
|
+
"Trace ",
|
|
17146
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
17147
|
+
"# ",
|
|
17148
|
+
traceId
|
|
17149
|
+
] })
|
|
17150
|
+
] }),
|
|
17151
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.CloseButton, { onClick: onClose })
|
|
17152
|
+
] }),
|
|
17153
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.LoadingData, { children: "Loading trace..." }) : hierarchicalSpans.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.NoData, { children: "No spans found for this trace." }) : /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.Content, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
17154
|
+
TraceTimeline,
|
|
17155
|
+
{
|
|
17156
|
+
hierarchicalSpans,
|
|
17157
|
+
onSpanClick: handleSpanClick,
|
|
17158
|
+
selectedSpanId: selectedSpanId ?? void 0,
|
|
17159
|
+
expandedSpanIds,
|
|
17160
|
+
setExpandedSpanIds
|
|
17161
|
+
}
|
|
17162
|
+
) })
|
|
17163
|
+
] });
|
|
17164
|
+
}
|
|
17165
|
+
|
|
17166
|
+
function TracesLayout({
|
|
17167
|
+
listSlot,
|
|
17168
|
+
tracePanelSlot,
|
|
17169
|
+
spanPanelSlot,
|
|
17170
|
+
scorePanelSlot,
|
|
17171
|
+
traceCollapsed
|
|
17172
|
+
}) {
|
|
17173
|
+
const hasSidePanel = !!tracePanelSlot;
|
|
17174
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17175
|
+
"div",
|
|
17176
|
+
{
|
|
17177
|
+
className: cn(
|
|
17178
|
+
"grid max-h-full min-h-0 gap-4 items-start ",
|
|
17179
|
+
hasSidePanel ? "grid-cols-[1fr_1fr]" : "grid-cols-[1fr]"
|
|
17180
|
+
),
|
|
17181
|
+
children: [
|
|
17182
|
+
listSlot,
|
|
17183
|
+
hasSidePanel && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17184
|
+
"div",
|
|
17185
|
+
{
|
|
17186
|
+
className: cn(
|
|
17187
|
+
"grid gap-4 max-h-full overflow-auto",
|
|
17188
|
+
scorePanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr_3fr]" : "grid-rows-[2fr_3fr_3fr]" : spanPanelSlot ? traceCollapsed ? "grid-rows-[auto_3fr]" : "grid-rows-[2fr_3fr]" : traceCollapsed ? "grid-rows-[auto]" : "grid-rows-[1fr]"
|
|
17189
|
+
),
|
|
17190
|
+
children: [
|
|
17191
|
+
tracePanelSlot,
|
|
17192
|
+
spanPanelSlot,
|
|
17193
|
+
scorePanelSlot
|
|
17194
|
+
]
|
|
17195
|
+
}
|
|
17196
|
+
)
|
|
17197
|
+
]
|
|
17198
|
+
}
|
|
17199
|
+
);
|
|
17200
|
+
}
|
|
17201
|
+
|
|
17202
|
+
function groupTracesByThread(traces) {
|
|
17203
|
+
const threadMap = /* @__PURE__ */ new Map();
|
|
17204
|
+
const ungrouped = [];
|
|
17205
|
+
for (const trace of traces) {
|
|
17206
|
+
if (trace.threadId) {
|
|
17207
|
+
const existing = threadMap.get(trace.threadId);
|
|
17208
|
+
if (existing) {
|
|
17209
|
+
existing.push(trace);
|
|
17210
|
+
} else {
|
|
17211
|
+
threadMap.set(trace.threadId, [trace]);
|
|
17212
|
+
}
|
|
17213
|
+
} else {
|
|
17214
|
+
ungrouped.push(trace);
|
|
17215
|
+
}
|
|
17216
|
+
}
|
|
17217
|
+
const groups = Array.from(threadMap.entries()).map(([threadId, traces2]) => ({
|
|
17218
|
+
threadId,
|
|
17219
|
+
traces: traces2
|
|
17220
|
+
}));
|
|
17221
|
+
groups.sort((a, b) => {
|
|
17222
|
+
const aLatest = Math.max(...a.traces.map((t) => new Date(t.createdAt).getTime()));
|
|
17223
|
+
const bLatest = Math.max(...b.traces.map((t) => new Date(t.createdAt).getTime()));
|
|
17224
|
+
return bLatest - aLatest;
|
|
17225
|
+
});
|
|
17226
|
+
return { groups, ungrouped };
|
|
17227
|
+
}
|
|
17228
|
+
|
|
17229
|
+
const COLUMNS$1 = "auto auto auto auto minmax(5rem,1fr) auto auto";
|
|
17230
|
+
function TracesListView({
|
|
17231
|
+
traces,
|
|
17232
|
+
isLoading,
|
|
17233
|
+
isFetchingNextPage,
|
|
17234
|
+
hasNextPage,
|
|
17235
|
+
setEndOfListElement,
|
|
17236
|
+
filtersApplied,
|
|
17237
|
+
featuredTraceId,
|
|
17238
|
+
onTraceClick,
|
|
17239
|
+
groupByThread,
|
|
17240
|
+
threadTitles
|
|
17241
|
+
}) {
|
|
17242
|
+
if (isLoading) {
|
|
17243
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataListSkeleton, { columns: COLUMNS$1 });
|
|
17244
|
+
}
|
|
17245
|
+
const renderRows = (rows) => rows.map((trace) => {
|
|
17246
|
+
const isFeatured = trace.traceId === featuredTraceId;
|
|
17247
|
+
const displayDate = trace.startedAt ?? trace.createdAt;
|
|
17248
|
+
const entityName = trace.entityName || trace.entityId || trace.attributes?.agentId || trace.attributes?.workflowId;
|
|
17249
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17250
|
+
TracesDataList.RowButton,
|
|
17251
|
+
{
|
|
17252
|
+
onClick: () => onTraceClick(trace),
|
|
17253
|
+
className: cn(isFeatured && "bg-surface4"),
|
|
17254
|
+
children: [
|
|
17255
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.IdCell, { traceId: trace.traceId }),
|
|
17256
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.DateCell, { timestamp: displayDate }),
|
|
17257
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TimeCell, { timestamp: displayDate }),
|
|
17258
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.NameCell, { name: trace.name }),
|
|
17259
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.InputCell, { input: getInputPreview(trace.input) }),
|
|
17260
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.EntityCell, { entityType: trace.entityType, entityName }),
|
|
17261
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.StatusCell, { status: trace.attributes?.status })
|
|
17262
|
+
]
|
|
17263
|
+
},
|
|
17264
|
+
trace.traceId
|
|
17265
|
+
);
|
|
17266
|
+
});
|
|
17267
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(TracesDataList, { columns: COLUMNS$1, className: "min-w-0", children: [
|
|
17268
|
+
/* @__PURE__ */ jsxRuntime.jsxs(TracesDataList.Top, { children: [
|
|
17269
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "ID" }),
|
|
17270
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Date" }),
|
|
17271
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Time" }),
|
|
17272
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Name" }),
|
|
17273
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Input" }),
|
|
17274
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Entity" }),
|
|
17275
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.TopCell, { children: "Status" })
|
|
17276
|
+
] }),
|
|
17277
|
+
traces.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
17278
|
+
TracesDataList.NoMatch,
|
|
17279
|
+
{
|
|
17280
|
+
message: filtersApplied ? "No traces found for applied filters" : "No traces found yet"
|
|
17281
|
+
}
|
|
17282
|
+
) : groupByThread ? (() => {
|
|
17283
|
+
const { groups, ungrouped } = groupTracesByThread(traces);
|
|
17284
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17285
|
+
groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React.Fragment, { children: [
|
|
17286
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.Subheader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TracesDataList.SubHeading, { className: "flex gap-2", children: [
|
|
17287
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "uppercase", children: "Thread" }),
|
|
17288
|
+
threadTitles?.[group.threadId] && /* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
17289
|
+
"'",
|
|
17290
|
+
threadTitles[group.threadId],
|
|
17291
|
+
"'"
|
|
17292
|
+
] }),
|
|
17293
|
+
/* @__PURE__ */ jsxRuntime.jsxs("b", { children: [
|
|
17294
|
+
"# ",
|
|
17295
|
+
group.threadId
|
|
17296
|
+
] }),
|
|
17297
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-neutral2", children: [
|
|
17298
|
+
"(",
|
|
17299
|
+
group.traces.length,
|
|
17300
|
+
")"
|
|
17301
|
+
] })
|
|
17302
|
+
] }) }),
|
|
17303
|
+
renderRows(group.traces)
|
|
17304
|
+
] }, group.threadId)),
|
|
17305
|
+
ungrouped.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
17306
|
+
/* @__PURE__ */ jsxRuntime.jsx(TracesDataList.Subheader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TracesDataList.SubHeading, { className: "flex gap-2 uppercase", children: [
|
|
17307
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "No thread" }),
|
|
17308
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-neutral2", children: [
|
|
17309
|
+
"(",
|
|
17310
|
+
ungrouped.length,
|
|
17311
|
+
")"
|
|
17312
|
+
] })
|
|
17313
|
+
] }) }),
|
|
17314
|
+
renderRows(ungrouped)
|
|
17315
|
+
] })
|
|
17316
|
+
] });
|
|
17317
|
+
})() : renderRows(traces),
|
|
17318
|
+
traces.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17319
|
+
TracesDataList.NextPageLoading,
|
|
17320
|
+
{
|
|
17321
|
+
isLoading: isFetchingNextPage,
|
|
17322
|
+
hasMore: hasNextPage,
|
|
17323
|
+
setEndOfListElement
|
|
17324
|
+
}
|
|
17325
|
+
)
|
|
17326
|
+
] });
|
|
17327
|
+
}
|
|
17328
|
+
|
|
17329
|
+
function TracesErrorContent({ error, resource, errorTitle }) {
|
|
17330
|
+
if (is401UnauthorizedError(error)) return /* @__PURE__ */ jsxRuntime.jsx(SessionExpired, {});
|
|
17331
|
+
if (is403ForbiddenError(error)) return /* @__PURE__ */ jsxRuntime.jsx(PermissionDenied, { resource });
|
|
17332
|
+
const parsed = error instanceof Error ? parseError(error) : void 0;
|
|
17333
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { title: errorTitle, message: parsed?.error ?? "Unknown error" });
|
|
17334
|
+
}
|
|
17335
|
+
|
|
17336
|
+
const IMMUTABLE_CACHE_TIME$1 = 1e3 * 60 * 60 * 24 * 30;
|
|
17337
|
+
function useSpanDetail(traceId, spanId) {
|
|
17338
|
+
const client = react.useMastraClient();
|
|
17339
|
+
return reactQuery.useQuery({
|
|
17340
|
+
queryKey: ["span-detail", traceId, spanId],
|
|
17341
|
+
queryFn: async () => {
|
|
17342
|
+
if (!traceId || !spanId) {
|
|
17343
|
+
throw new Error("Trace ID and Span ID are required");
|
|
17344
|
+
}
|
|
17345
|
+
return client.getSpan(traceId, spanId);
|
|
17346
|
+
},
|
|
17347
|
+
enabled: !!traceId && !!spanId,
|
|
17348
|
+
staleTime: (query) => {
|
|
17349
|
+
const data = query.state.data;
|
|
17350
|
+
if (data?.span?.endedAt) {
|
|
17351
|
+
return IMMUTABLE_CACHE_TIME$1;
|
|
17352
|
+
}
|
|
17353
|
+
return 0;
|
|
17354
|
+
}
|
|
17355
|
+
});
|
|
17356
|
+
}
|
|
17357
|
+
|
|
17358
|
+
const IMMUTABLE_CACHE_TIME = 1e3 * 60 * 60 * 24 * 30;
|
|
17359
|
+
function useTraceLightSpans(traceId) {
|
|
17360
|
+
const client = react.useMastraClient();
|
|
17361
|
+
return reactQuery.useQuery({
|
|
17362
|
+
queryKey: ["trace-light-spans", traceId],
|
|
17363
|
+
queryFn: async () => {
|
|
17364
|
+
if (!traceId) {
|
|
17365
|
+
throw new Error("Trace ID is required");
|
|
17366
|
+
}
|
|
17367
|
+
const res = await client.getTraceLight(traceId);
|
|
17368
|
+
return res;
|
|
17369
|
+
},
|
|
17370
|
+
enabled: !!traceId,
|
|
17371
|
+
staleTime: (query) => {
|
|
17372
|
+
const data = query.state.data;
|
|
17373
|
+
const isFinished = data?.spans.every((d) => Boolean(d.endedAt));
|
|
17374
|
+
if (isFinished) {
|
|
17375
|
+
return IMMUTABLE_CACHE_TIME;
|
|
17376
|
+
}
|
|
17377
|
+
return 0;
|
|
17378
|
+
}
|
|
17379
|
+
});
|
|
17380
|
+
}
|
|
17381
|
+
|
|
17382
|
+
function useTraceSpans(traceId) {
|
|
17383
|
+
const client = react.useMastraClient();
|
|
17384
|
+
return reactQuery.useQuery({
|
|
17385
|
+
queryKey: ["trace-spans", traceId],
|
|
17386
|
+
queryFn: async () => {
|
|
17387
|
+
if (!traceId) {
|
|
17388
|
+
throw new Error("Trace ID is required");
|
|
17389
|
+
}
|
|
17390
|
+
const res = await client.getTrace(traceId);
|
|
17391
|
+
return res;
|
|
17392
|
+
},
|
|
17393
|
+
enabled: !!traceId
|
|
17394
|
+
});
|
|
17395
|
+
}
|
|
17396
|
+
|
|
17397
|
+
const fetchTracesFn = async ({
|
|
17398
|
+
client,
|
|
17399
|
+
page,
|
|
17400
|
+
perPage,
|
|
17401
|
+
filters
|
|
17402
|
+
}) => {
|
|
17403
|
+
return client.listTraces({
|
|
17404
|
+
pagination: {
|
|
17405
|
+
page,
|
|
17406
|
+
perPage
|
|
17407
|
+
},
|
|
17408
|
+
filters
|
|
17409
|
+
});
|
|
17410
|
+
};
|
|
17411
|
+
const TRACES_PER_PAGE = 25;
|
|
17412
|
+
function getTracesNextPageParam(lastPage, _allPages, lastPageParam) {
|
|
17413
|
+
if (lastPage?.pagination?.hasMore) {
|
|
17414
|
+
return lastPageParam + 1;
|
|
17415
|
+
}
|
|
17416
|
+
return void 0;
|
|
17417
|
+
}
|
|
17418
|
+
function selectUniqueTraces(data) {
|
|
17419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17420
|
+
const spans = data.pages.flatMap((page) => page.spans ?? []).filter((span) => {
|
|
17421
|
+
if (seen.has(span.traceId)) return false;
|
|
17422
|
+
seen.add(span.traceId);
|
|
17423
|
+
return true;
|
|
17424
|
+
});
|
|
17425
|
+
const threadTitles = {};
|
|
17426
|
+
for (const page of data.pages) {
|
|
17427
|
+
if (page.threadTitles) {
|
|
17428
|
+
Object.assign(threadTitles, page.threadTitles);
|
|
17429
|
+
}
|
|
17430
|
+
}
|
|
17431
|
+
return { spans, threadTitles };
|
|
17432
|
+
}
|
|
17433
|
+
const useTraces = ({ filters }) => {
|
|
17434
|
+
const client = react.useMastraClient();
|
|
17435
|
+
const { inView: isEndOfListInView, setRef: setEndOfListElement } = useInView();
|
|
17436
|
+
const query = reactQuery.useInfiniteQuery({
|
|
17437
|
+
queryKey: ["traces", filters],
|
|
17438
|
+
queryFn: ({ pageParam }) => fetchTracesFn({
|
|
17439
|
+
client,
|
|
17440
|
+
page: pageParam,
|
|
17441
|
+
perPage: TRACES_PER_PAGE,
|
|
17442
|
+
filters
|
|
17443
|
+
}),
|
|
17444
|
+
initialPageParam: 0,
|
|
17445
|
+
getNextPageParam: getTracesNextPageParam,
|
|
17446
|
+
select: selectUniqueTraces,
|
|
17447
|
+
placeholderData: reactQuery.keepPreviousData,
|
|
17448
|
+
retry: false,
|
|
17449
|
+
// Disable polling on 403 to prevent flickering
|
|
17450
|
+
refetchInterval: (query2) => is403ForbiddenError(query2.state.error) ? false : 3e3
|
|
17451
|
+
});
|
|
17452
|
+
const { hasNextPage, isFetchingNextPage, fetchNextPage } = query;
|
|
17453
|
+
React.useEffect(() => {
|
|
17454
|
+
if (isEndOfListInView && hasNextPage && !isFetchingNextPage) {
|
|
17455
|
+
void fetchNextPage();
|
|
17456
|
+
}
|
|
17457
|
+
}, [isEndOfListInView, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
17458
|
+
return { ...query, setEndOfListElement };
|
|
17459
|
+
};
|
|
17460
|
+
|
|
17461
|
+
const useTags = () => {
|
|
17462
|
+
const client = react.useMastraClient();
|
|
17463
|
+
return reactQuery.useQuery({
|
|
17464
|
+
queryKey: ["observability-tags"],
|
|
17465
|
+
queryFn: async () => {
|
|
17466
|
+
try {
|
|
17467
|
+
return await client.getTags();
|
|
17468
|
+
} catch {
|
|
17469
|
+
return { tags: [] };
|
|
17470
|
+
}
|
|
17471
|
+
},
|
|
17472
|
+
select: (data) => data?.tags ?? [],
|
|
17473
|
+
retry: false
|
|
17474
|
+
});
|
|
17475
|
+
};
|
|
17476
|
+
|
|
17477
|
+
const ROOT_ENTITY_TYPES = {
|
|
17478
|
+
AGENT: observability.EntityType.AGENT,
|
|
17479
|
+
WORKFLOW: observability.EntityType.WORKFLOW_RUN,
|
|
17480
|
+
SCORER: observability.EntityType.SCORER,
|
|
17481
|
+
INGEST: observability.EntityType.RAG_INGESTION
|
|
17482
|
+
};
|
|
17483
|
+
const ROOT_ENTITY_TYPE_OPTIONS = [
|
|
17484
|
+
{ label: "Agent", entityType: ROOT_ENTITY_TYPES.AGENT },
|
|
17485
|
+
{ label: "Workflow", entityType: ROOT_ENTITY_TYPES.WORKFLOW },
|
|
17486
|
+
{ label: "Scorer", entityType: ROOT_ENTITY_TYPES.SCORER },
|
|
17487
|
+
{ label: "Ingest", entityType: ROOT_ENTITY_TYPES.INGEST }
|
|
17488
|
+
];
|
|
17489
|
+
const TRACE_STATUS_OPTIONS = [
|
|
17490
|
+
{ label: "Running", value: "running" },
|
|
17491
|
+
{ label: "Success", value: "success" },
|
|
17492
|
+
{ label: "Error", value: "error" }
|
|
17493
|
+
];
|
|
17494
|
+
const TRACE_SYNTHETIC_FILTER_FIELD_IDS = ["rootEntityType", "status"];
|
|
17495
|
+
const TRACE_ROOT_ENTITY_TYPE_PARAM = "rootEntityType";
|
|
17496
|
+
const TRACE_STATUS_PARAM = "status";
|
|
17497
|
+
const TRACE_DATE_PRESET_PARAM = "datePreset";
|
|
17498
|
+
const TRACE_DATE_FROM_PARAM = "dateFrom";
|
|
17499
|
+
const TRACE_DATE_TO_PARAM = "dateTo";
|
|
17500
|
+
const TRACE_DATE_PRESET_VALUES = /* @__PURE__ */ new Set([
|
|
17501
|
+
"all",
|
|
17502
|
+
"last-24h",
|
|
17503
|
+
"last-3d",
|
|
17504
|
+
"last-7d",
|
|
17505
|
+
"last-14d",
|
|
17506
|
+
"last-30d",
|
|
17507
|
+
"custom"
|
|
17508
|
+
]);
|
|
17509
|
+
const TRACE_PROPERTY_FILTER_PARAM_BY_FIELD = {
|
|
17510
|
+
tags: "filterTags",
|
|
17511
|
+
entityId: "filterEntityId",
|
|
17512
|
+
entityName: "filterEntityName",
|
|
17513
|
+
traceId: "filterTraceId",
|
|
17514
|
+
runId: "filterRunId",
|
|
17515
|
+
threadId: "filterThreadId",
|
|
17516
|
+
sessionId: "filterSessionId",
|
|
17517
|
+
requestId: "filterRequestId",
|
|
17518
|
+
resourceId: "filterResourceId",
|
|
17519
|
+
userId: "filterUserId",
|
|
17520
|
+
organizationId: "filterOrganizationId",
|
|
17521
|
+
serviceName: "filterServiceName",
|
|
17522
|
+
environment: "filterEnvironment",
|
|
17523
|
+
experimentId: "filterExperimentId"
|
|
17524
|
+
};
|
|
17525
|
+
const TRACE_PROPERTY_FILTER_FIELD_IDS = Object.keys(TRACE_PROPERTY_FILTER_PARAM_BY_FIELD);
|
|
17526
|
+
const TRACE_STATUS_VALUES = /* @__PURE__ */ new Set(["running", "success", "error"]);
|
|
17527
|
+
const DEFAULT_TRACE_FILTERS_STORAGE_KEY = "mastra:traces:saved-filters";
|
|
17528
|
+
function saveTraceFiltersToStorage(params, storageKey = DEFAULT_TRACE_FILTERS_STORAGE_KEY) {
|
|
17529
|
+
const serialized = getPreservedTraceFilterParams(params);
|
|
17530
|
+
const preset = params.get(TRACE_DATE_PRESET_PARAM);
|
|
17531
|
+
if (preset) serialized.set(TRACE_DATE_PRESET_PARAM, preset);
|
|
17532
|
+
const from = params.get(TRACE_DATE_FROM_PARAM);
|
|
17533
|
+
if (from) serialized.set(TRACE_DATE_FROM_PARAM, from);
|
|
17534
|
+
const to = params.get(TRACE_DATE_TO_PARAM);
|
|
17535
|
+
if (to) serialized.set(TRACE_DATE_TO_PARAM, to);
|
|
17536
|
+
try {
|
|
17537
|
+
localStorage.setItem(storageKey, serialized.toString());
|
|
17538
|
+
} catch {
|
|
17539
|
+
}
|
|
17540
|
+
}
|
|
17541
|
+
function clearSavedTraceFilters(storageKey = DEFAULT_TRACE_FILTERS_STORAGE_KEY) {
|
|
17542
|
+
try {
|
|
17543
|
+
localStorage.removeItem(storageKey);
|
|
17544
|
+
} catch {
|
|
17545
|
+
}
|
|
17546
|
+
}
|
|
17547
|
+
function loadTraceFiltersFromStorage(storageKey = DEFAULT_TRACE_FILTERS_STORAGE_KEY) {
|
|
17548
|
+
try {
|
|
17549
|
+
const raw = localStorage.getItem(storageKey);
|
|
17550
|
+
if (!raw) return null;
|
|
17551
|
+
const parsed = new URLSearchParams(raw);
|
|
17552
|
+
return parsed.toString() ? parsed : null;
|
|
17553
|
+
} catch {
|
|
17554
|
+
return null;
|
|
17555
|
+
}
|
|
17556
|
+
}
|
|
17557
|
+
function hasAnyTraceFilterParams(params) {
|
|
17558
|
+
if (params.has(TRACE_DATE_PRESET_PARAM)) return true;
|
|
17559
|
+
if (params.has(TRACE_DATE_FROM_PARAM)) return true;
|
|
17560
|
+
if (params.has(TRACE_DATE_TO_PARAM)) return true;
|
|
17561
|
+
if (params.has(TRACE_ROOT_ENTITY_TYPE_PARAM)) return true;
|
|
17562
|
+
if (params.has(TRACE_STATUS_PARAM)) return true;
|
|
17563
|
+
for (const fieldId of TRACE_PROPERTY_FILTER_FIELD_IDS) {
|
|
17564
|
+
if (params.has(TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId])) return true;
|
|
17565
|
+
}
|
|
17566
|
+
return false;
|
|
17567
|
+
}
|
|
17568
|
+
function createTracePropertyFilterFields({
|
|
17569
|
+
availableTags,
|
|
17570
|
+
availableRootEntityNames,
|
|
17571
|
+
availableServiceNames,
|
|
17572
|
+
availableEnvironments,
|
|
17573
|
+
loading
|
|
17574
|
+
}) {
|
|
17575
|
+
const fields = [
|
|
17576
|
+
{
|
|
17577
|
+
id: "rootEntityType",
|
|
17578
|
+
label: "Primitive Type",
|
|
17579
|
+
kind: "pick-multi",
|
|
17580
|
+
searchable: false,
|
|
17581
|
+
options: ROOT_ENTITY_TYPE_OPTIONS.map((o) => ({ label: o.label, value: o.entityType })),
|
|
17582
|
+
placeholder: "Choose entity type",
|
|
17583
|
+
emptyText: "No entity types."
|
|
17584
|
+
},
|
|
17585
|
+
{
|
|
17586
|
+
id: "entityName",
|
|
17587
|
+
label: "Primitive Name",
|
|
17588
|
+
kind: "pick-multi",
|
|
17589
|
+
options: availableRootEntityNames.map((name) => ({ label: name, value: name })),
|
|
17590
|
+
placeholder: "Choose entity names",
|
|
17591
|
+
emptyText: "No entity names found.",
|
|
17592
|
+
isLoading: loading?.entityNames
|
|
17593
|
+
},
|
|
17594
|
+
{ id: "entityId", label: "Primitive ID", kind: "text" },
|
|
17595
|
+
{
|
|
17596
|
+
id: "status",
|
|
17597
|
+
label: "Status",
|
|
17598
|
+
kind: "pick-multi",
|
|
17599
|
+
searchable: false,
|
|
17600
|
+
options: TRACE_STATUS_OPTIONS.map((o) => ({ label: o.label, value: o.value })),
|
|
17601
|
+
placeholder: "Choose status",
|
|
17602
|
+
emptyText: "No statuses."
|
|
17603
|
+
},
|
|
17604
|
+
{
|
|
17605
|
+
id: "tags",
|
|
17606
|
+
label: "Tags",
|
|
17607
|
+
kind: "pick-multi",
|
|
17608
|
+
multi: true,
|
|
17609
|
+
options: availableTags.map((tag) => ({ label: tag, value: tag })),
|
|
17610
|
+
placeholder: "Choose tags",
|
|
17611
|
+
emptyText: "No tags found.",
|
|
17612
|
+
isLoading: loading?.tags
|
|
17613
|
+
},
|
|
17614
|
+
{
|
|
17615
|
+
id: "serviceName",
|
|
17616
|
+
label: "Service Name",
|
|
17617
|
+
kind: "pick-multi",
|
|
17618
|
+
options: availableServiceNames.map((name) => ({ label: name, value: name })),
|
|
17619
|
+
placeholder: "Choose service names",
|
|
17620
|
+
emptyText: "No service names found.",
|
|
17621
|
+
isLoading: loading?.serviceNames
|
|
17622
|
+
},
|
|
17623
|
+
{
|
|
17624
|
+
id: "environment",
|
|
17625
|
+
label: "Environment",
|
|
17626
|
+
kind: "pick-multi",
|
|
17627
|
+
options: availableEnvironments.map((env) => ({ label: env, value: env })),
|
|
17628
|
+
placeholder: "Choose environments",
|
|
17629
|
+
emptyText: "No environments found.",
|
|
17630
|
+
isLoading: loading?.environments
|
|
17631
|
+
},
|
|
17632
|
+
{ id: "traceId", label: "Trace ID", kind: "text" },
|
|
17633
|
+
{ id: "runId", label: "Run ID", kind: "text" },
|
|
17634
|
+
{ id: "threadId", label: "Thread ID", kind: "text" },
|
|
17635
|
+
{ id: "sessionId", label: "Session ID", kind: "text" },
|
|
17636
|
+
{ id: "requestId", label: "Request ID", kind: "text" },
|
|
17637
|
+
{ id: "resourceId", label: "Resource ID", kind: "text" },
|
|
17638
|
+
{ id: "userId", label: "User ID", kind: "text" },
|
|
17639
|
+
{ id: "organizationId", label: "Organization ID", kind: "text" },
|
|
17640
|
+
{ id: "experimentId", label: "Experiment ID", kind: "text" }
|
|
17641
|
+
];
|
|
17642
|
+
const byLabel = (a, b) => a.label.localeCompare(b.label);
|
|
17643
|
+
const pickMulti = fields.filter((f) => f.kind === "pick-multi").sort(byLabel);
|
|
17644
|
+
const text = fields.filter((f) => f.kind === "text").sort(byLabel);
|
|
17645
|
+
return [...pickMulti, ...text];
|
|
17646
|
+
}
|
|
17647
|
+
function getTracePropertyFilterTokens(searchParams) {
|
|
17648
|
+
const tokens = [];
|
|
17649
|
+
const paramToFieldId = /* @__PURE__ */ new Map([
|
|
17650
|
+
[TRACE_ROOT_ENTITY_TYPE_PARAM, "rootEntityType"],
|
|
17651
|
+
[TRACE_STATUS_PARAM, "status"]
|
|
17652
|
+
]);
|
|
17653
|
+
for (const fieldId of TRACE_PROPERTY_FILTER_FIELD_IDS) {
|
|
17654
|
+
paramToFieldId.set(TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId], fieldId);
|
|
17655
|
+
}
|
|
17656
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17657
|
+
for (const [paramName] of searchParams.entries()) {
|
|
17658
|
+
const fieldId = paramToFieldId.get(paramName);
|
|
17659
|
+
if (!fieldId || seen.has(fieldId)) continue;
|
|
17660
|
+
seen.add(fieldId);
|
|
17661
|
+
if (fieldId === "tags") {
|
|
17662
|
+
const raw = searchParams.getAll(paramName);
|
|
17663
|
+
if (raw.length === 0) continue;
|
|
17664
|
+
tokens.push({ fieldId, value: raw.filter(Boolean) });
|
|
17665
|
+
continue;
|
|
17666
|
+
}
|
|
17667
|
+
const value = searchParams.get(paramName);
|
|
17668
|
+
if (value !== null) tokens.push({ fieldId, value });
|
|
17669
|
+
}
|
|
17670
|
+
return tokens;
|
|
17671
|
+
}
|
|
17672
|
+
function getPreservedTraceFilterParams(searchParams) {
|
|
17673
|
+
const next = new URLSearchParams();
|
|
17674
|
+
const rootEntityType = searchParams.get(TRACE_ROOT_ENTITY_TYPE_PARAM);
|
|
17675
|
+
if (rootEntityType) next.set(TRACE_ROOT_ENTITY_TYPE_PARAM, rootEntityType);
|
|
17676
|
+
const status = searchParams.get(TRACE_STATUS_PARAM);
|
|
17677
|
+
if (status) next.set(TRACE_STATUS_PARAM, status);
|
|
17678
|
+
for (const fieldId of TRACE_PROPERTY_FILTER_FIELD_IDS) {
|
|
17679
|
+
const param = TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId];
|
|
17680
|
+
if (fieldId === "tags") {
|
|
17681
|
+
for (const value2 of searchParams.getAll(param)) {
|
|
17682
|
+
next.append(param, value2);
|
|
17683
|
+
}
|
|
17684
|
+
continue;
|
|
17685
|
+
}
|
|
17686
|
+
const value = searchParams.get(param);
|
|
17687
|
+
if (value) {
|
|
17688
|
+
next.set(param, value);
|
|
17689
|
+
}
|
|
17690
|
+
}
|
|
17691
|
+
return next;
|
|
17692
|
+
}
|
|
17693
|
+
function applyTracePropertyFilterTokens(params, tokens) {
|
|
17694
|
+
params.delete(TRACE_ROOT_ENTITY_TYPE_PARAM);
|
|
17695
|
+
params.delete(TRACE_STATUS_PARAM);
|
|
17696
|
+
for (const fieldId of TRACE_PROPERTY_FILTER_FIELD_IDS) {
|
|
17697
|
+
params.delete(TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId]);
|
|
17698
|
+
}
|
|
17699
|
+
for (const token of tokens) {
|
|
17700
|
+
if (token.fieldId === "rootEntityType" && typeof token.value === "string") {
|
|
17701
|
+
params.set(TRACE_ROOT_ENTITY_TYPE_PARAM, token.value);
|
|
17702
|
+
continue;
|
|
17703
|
+
}
|
|
17704
|
+
if (token.fieldId === "status" && typeof token.value === "string") {
|
|
17705
|
+
params.set(TRACE_STATUS_PARAM, token.value);
|
|
17706
|
+
continue;
|
|
17707
|
+
}
|
|
17708
|
+
const param = TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[token.fieldId];
|
|
17709
|
+
if (!param) continue;
|
|
17710
|
+
if (token.fieldId === "tags" && Array.isArray(token.value)) {
|
|
17711
|
+
if (token.value.length === 0) {
|
|
17712
|
+
params.append(param, "");
|
|
17713
|
+
} else {
|
|
17714
|
+
for (const value of token.value) {
|
|
17715
|
+
params.append(param, value);
|
|
17716
|
+
}
|
|
17717
|
+
}
|
|
17718
|
+
continue;
|
|
17719
|
+
}
|
|
17720
|
+
if (typeof token.value === "string") {
|
|
17721
|
+
params.set(param, token.value.trim());
|
|
17722
|
+
}
|
|
17723
|
+
}
|
|
17724
|
+
}
|
|
17725
|
+
function buildTraceListFilters({
|
|
17726
|
+
rootEntityType,
|
|
17727
|
+
status,
|
|
17728
|
+
dateFrom,
|
|
17729
|
+
dateTo,
|
|
17730
|
+
tokens
|
|
17731
|
+
}) {
|
|
17732
|
+
const filters = {};
|
|
17733
|
+
if (rootEntityType) {
|
|
17734
|
+
filters.entityType = rootEntityType;
|
|
17735
|
+
}
|
|
17736
|
+
if (status) {
|
|
17737
|
+
filters.status = status;
|
|
17738
|
+
}
|
|
17739
|
+
if (dateFrom) {
|
|
17740
|
+
filters.startedAt = { start: dateFrom };
|
|
17741
|
+
}
|
|
17742
|
+
if (dateTo) {
|
|
17743
|
+
filters.endedAt = { end: dateTo };
|
|
17744
|
+
}
|
|
17745
|
+
for (const token of tokens) {
|
|
17746
|
+
if (token.fieldId === "tags") {
|
|
17747
|
+
if (Array.isArray(token.value) && token.value.length > 0) {
|
|
17748
|
+
filters.tags = token.value;
|
|
17749
|
+
} else if (typeof token.value === "string" && token.value.trim()) {
|
|
17750
|
+
filters.tags = [token.value.trim()];
|
|
17751
|
+
}
|
|
17752
|
+
continue;
|
|
17753
|
+
}
|
|
17754
|
+
if (typeof token.value !== "string") continue;
|
|
17755
|
+
if (!token.value.trim()) continue;
|
|
17756
|
+
if (token.value === "Any") continue;
|
|
17757
|
+
switch (token.fieldId) {
|
|
17758
|
+
case "entityId":
|
|
17759
|
+
filters.entityId = token.value;
|
|
17760
|
+
break;
|
|
17761
|
+
case "entityName":
|
|
17762
|
+
filters.entityName = token.value;
|
|
17763
|
+
break;
|
|
17764
|
+
case "traceId":
|
|
17765
|
+
filters.traceId = token.value;
|
|
17766
|
+
break;
|
|
17767
|
+
case "runId":
|
|
17768
|
+
filters.runId = token.value;
|
|
17769
|
+
break;
|
|
17770
|
+
case "threadId":
|
|
17771
|
+
filters.threadId = token.value;
|
|
17772
|
+
break;
|
|
17773
|
+
case "sessionId":
|
|
17774
|
+
filters.sessionId = token.value;
|
|
17775
|
+
break;
|
|
17776
|
+
case "requestId":
|
|
17777
|
+
filters.requestId = token.value;
|
|
17778
|
+
break;
|
|
17779
|
+
case "resourceId":
|
|
17780
|
+
filters.resourceId = token.value;
|
|
17781
|
+
break;
|
|
17782
|
+
case "userId":
|
|
17783
|
+
filters.userId = token.value;
|
|
17784
|
+
break;
|
|
17785
|
+
case "organizationId":
|
|
17786
|
+
filters.organizationId = token.value;
|
|
17787
|
+
break;
|
|
17788
|
+
case "serviceName":
|
|
17789
|
+
filters.serviceName = token.value;
|
|
17790
|
+
break;
|
|
17791
|
+
case "environment":
|
|
17792
|
+
filters.environment = token.value;
|
|
17793
|
+
break;
|
|
17794
|
+
case "experimentId":
|
|
17795
|
+
filters.experimentId = token.value;
|
|
17796
|
+
break;
|
|
17797
|
+
}
|
|
17798
|
+
}
|
|
17799
|
+
return filters;
|
|
17800
|
+
}
|
|
17801
|
+
function neutralizeFilterTokens(filterFields, filterTokens) {
|
|
17802
|
+
return filterTokens.map((token) => {
|
|
17803
|
+
const field = filterFields.find((f) => f.id === token.fieldId);
|
|
17804
|
+
if (!field) return token;
|
|
17805
|
+
if (field.kind === "text") return { fieldId: token.fieldId, value: "" };
|
|
17806
|
+
if (field.kind === "pick-multi") {
|
|
17807
|
+
return field.multi ? { fieldId: token.fieldId, value: [] } : { fieldId: token.fieldId, value: "Any" };
|
|
17808
|
+
}
|
|
17809
|
+
return token;
|
|
17810
|
+
});
|
|
17811
|
+
}
|
|
17812
|
+
|
|
17813
|
+
const useEntityNames = ({ entityType, rootOnly = false } = {}) => {
|
|
17814
|
+
const client = react.useMastraClient();
|
|
17815
|
+
const queryKey = entityType ? ["observability-entity-names", "by-type", entityType] : ["observability-entity-names", "all", rootOnly ? "root-only" : "all-types"];
|
|
17816
|
+
return reactQuery.useQuery({
|
|
17817
|
+
queryKey,
|
|
17818
|
+
queryFn: async () => {
|
|
17819
|
+
try {
|
|
17820
|
+
if (entityType) {
|
|
17821
|
+
return await client.getEntityNames({ entityType });
|
|
17822
|
+
}
|
|
17823
|
+
if (!rootOnly) {
|
|
17824
|
+
return await client.getEntityNames();
|
|
17825
|
+
}
|
|
17826
|
+
const responses = await Promise.all(
|
|
17827
|
+
ROOT_ENTITY_TYPE_OPTIONS.map((option) => client.getEntityNames({ entityType: option.entityType }))
|
|
17828
|
+
);
|
|
17829
|
+
return {
|
|
17830
|
+
names: Array.from(new Set(responses.flatMap((response) => response?.names ?? []))).sort()
|
|
17831
|
+
};
|
|
17832
|
+
} catch {
|
|
17833
|
+
return { names: [] };
|
|
17834
|
+
}
|
|
17835
|
+
},
|
|
17836
|
+
select: (data) => data?.names ?? [],
|
|
17837
|
+
retry: false
|
|
17838
|
+
});
|
|
17839
|
+
};
|
|
17840
|
+
|
|
17841
|
+
const useEnvironments = () => {
|
|
17842
|
+
const client = react.useMastraClient();
|
|
17843
|
+
return reactQuery.useQuery({
|
|
17844
|
+
queryKey: ["observability-environments"],
|
|
17845
|
+
queryFn: async () => {
|
|
17846
|
+
try {
|
|
17847
|
+
return await client.getEnvironments();
|
|
17848
|
+
} catch {
|
|
17849
|
+
return { environments: [] };
|
|
17850
|
+
}
|
|
17851
|
+
},
|
|
17852
|
+
select: (data) => data?.environments ?? [],
|
|
17853
|
+
retry: false
|
|
17854
|
+
});
|
|
17855
|
+
};
|
|
17856
|
+
|
|
17857
|
+
const useServiceNames = () => {
|
|
17858
|
+
const client = react.useMastraClient();
|
|
17859
|
+
return reactQuery.useQuery({
|
|
17860
|
+
queryKey: ["observability-service-names"],
|
|
17861
|
+
queryFn: async () => {
|
|
17862
|
+
try {
|
|
17863
|
+
return await client.getServiceNames();
|
|
17864
|
+
} catch {
|
|
17865
|
+
return { serviceNames: [] };
|
|
17866
|
+
}
|
|
17867
|
+
},
|
|
17868
|
+
select: (data) => data?.serviceNames ?? [],
|
|
17869
|
+
retry: false
|
|
17870
|
+
});
|
|
17871
|
+
};
|
|
17872
|
+
|
|
17873
|
+
function useTraceSpanNavigation(lightSpans, featuredSpanId, onSpanChange) {
|
|
17874
|
+
const timelineSpanIds = React.useMemo(() => getAllSpanIds(formatHierarchicalSpans(lightSpans ?? [])), [lightSpans]);
|
|
17875
|
+
const featuredSpanIdx = featuredSpanId ? timelineSpanIds.indexOf(featuredSpanId) : -1;
|
|
17876
|
+
const handlePreviousSpan = featuredSpanIdx > 0 ? () => onSpanChange(timelineSpanIds[featuredSpanIdx - 1]) : void 0;
|
|
17877
|
+
const handleNextSpan = featuredSpanIdx >= 0 && featuredSpanIdx < timelineSpanIds.length - 1 ? () => onSpanChange(timelineSpanIds[featuredSpanIdx + 1]) : void 0;
|
|
17878
|
+
return { handlePreviousSpan, handleNextSpan, timelineSpanIds };
|
|
17879
|
+
}
|
|
17880
|
+
|
|
17881
|
+
function useTraceListNavigation(traces, featuredTraceId, onTraceChange) {
|
|
17882
|
+
const featuredTraceIdx = React.useMemo(
|
|
17883
|
+
() => featuredTraceId ? traces.findIndex((t) => t.traceId === featuredTraceId) : -1,
|
|
17884
|
+
[traces, featuredTraceId]
|
|
17885
|
+
);
|
|
17886
|
+
const handlePreviousTrace = featuredTraceIdx > 0 ? () => onTraceChange(traces[featuredTraceIdx - 1].traceId) : void 0;
|
|
17887
|
+
const handleNextTrace = featuredTraceIdx >= 0 && featuredTraceIdx < traces.length - 1 ? () => onTraceChange(traces[featuredTraceIdx + 1].traceId) : void 0;
|
|
17888
|
+
return { featuredTraceIdx, handlePreviousTrace, handleNextTrace };
|
|
17889
|
+
}
|
|
17890
|
+
|
|
17891
|
+
const TRACE_ID_PARAM = "traceId";
|
|
17892
|
+
const SPAN_ID_PARAM = "spanId";
|
|
17893
|
+
const TAB_PARAM = "tab";
|
|
17894
|
+
const SCORE_ID_PARAM = "scoreId";
|
|
17895
|
+
const DAY_MS$1 = 24 * 60 * 60 * 1e3;
|
|
17896
|
+
const PRESET_MS$1 = {
|
|
17897
|
+
"last-24h": DAY_MS$1,
|
|
17898
|
+
"last-3d": 3 * DAY_MS$1,
|
|
17899
|
+
"last-7d": 7 * DAY_MS$1,
|
|
17900
|
+
"last-14d": 14 * DAY_MS$1,
|
|
17901
|
+
"last-30d": 30 * DAY_MS$1
|
|
17902
|
+
};
|
|
17903
|
+
function clearSelectionParams$1(params) {
|
|
17904
|
+
params.delete(TRACE_ID_PARAM);
|
|
17905
|
+
params.delete(SPAN_ID_PARAM);
|
|
17906
|
+
params.delete(TAB_PARAM);
|
|
17907
|
+
params.delete(SCORE_ID_PARAM);
|
|
17908
|
+
}
|
|
17909
|
+
function useTraceUrlState(searchParams, setSearchParams, options) {
|
|
17910
|
+
const { onRemoveAll } = options ?? {};
|
|
17911
|
+
const datePreset = React.useMemo(() => {
|
|
17912
|
+
const value = searchParams.get(TRACE_DATE_PRESET_PARAM);
|
|
17913
|
+
return value && TRACE_DATE_PRESET_VALUES.has(value) ? value : "last-24h";
|
|
17914
|
+
}, [searchParams]);
|
|
17915
|
+
const dateFromParamRaw = searchParams.get(TRACE_DATE_FROM_PARAM);
|
|
17916
|
+
const dateToParamRaw = searchParams.get(TRACE_DATE_TO_PARAM);
|
|
17917
|
+
const selectedDateFrom = React.useMemo(() => {
|
|
17918
|
+
if (datePreset === "custom") {
|
|
17919
|
+
if (!dateFromParamRaw) return void 0;
|
|
17920
|
+
const parsed = new Date(dateFromParamRaw);
|
|
17921
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
17922
|
+
}
|
|
17923
|
+
if (datePreset === "all") return void 0;
|
|
17924
|
+
const ms = PRESET_MS$1[datePreset];
|
|
17925
|
+
return ms ? new Date(Date.now() - ms) : void 0;
|
|
17926
|
+
}, [datePreset, dateFromParamRaw]);
|
|
17927
|
+
const selectedDateTo = React.useMemo(() => {
|
|
17928
|
+
if (datePreset !== "custom" || !dateToParamRaw) return void 0;
|
|
17929
|
+
const parsed = new Date(dateToParamRaw);
|
|
17930
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
17931
|
+
}, [datePreset, dateToParamRaw]);
|
|
17932
|
+
const datePresetRef = React.useRef(datePreset);
|
|
17933
|
+
datePresetRef.current = datePreset;
|
|
17934
|
+
const traceIdParam = searchParams.get(TRACE_ID_PARAM) || void 0;
|
|
17935
|
+
const spanIdParam = searchParams.get(SPAN_ID_PARAM) || void 0;
|
|
17936
|
+
const tabParam = searchParams.get(TAB_PARAM);
|
|
17937
|
+
const spanTabParam = tabParam === "scoring" ? "scoring" : tabParam === "details" ? "details" : void 0;
|
|
17938
|
+
const scoreIdParam = searchParams.get(SCORE_ID_PARAM) || void 0;
|
|
17939
|
+
const selectedEntityOption = React.useMemo(
|
|
17940
|
+
() => ROOT_ENTITY_TYPE_OPTIONS.find((option) => option.entityType === searchParams.get(TRACE_ROOT_ENTITY_TYPE_PARAM)),
|
|
17941
|
+
[searchParams]
|
|
17942
|
+
);
|
|
17943
|
+
const selectedStatus = React.useMemo(() => {
|
|
17944
|
+
const value = searchParams.get(TRACE_STATUS_PARAM);
|
|
17945
|
+
return value && TRACE_STATUS_VALUES.has(value) ? value : void 0;
|
|
17946
|
+
}, [searchParams]);
|
|
17947
|
+
const filterTokens = React.useMemo(() => getTracePropertyFilterTokens(searchParams), [searchParams]);
|
|
17948
|
+
const handleTraceClick = React.useCallback(
|
|
17949
|
+
(traceId) => {
|
|
17950
|
+
setSearchParams(
|
|
17951
|
+
(prev) => {
|
|
17952
|
+
const next = new URLSearchParams(prev);
|
|
17953
|
+
if (traceId) {
|
|
17954
|
+
next.set(TRACE_ID_PARAM, traceId);
|
|
17955
|
+
} else {
|
|
17956
|
+
next.delete(TRACE_ID_PARAM);
|
|
17957
|
+
}
|
|
17958
|
+
next.delete(SPAN_ID_PARAM);
|
|
17959
|
+
next.delete(TAB_PARAM);
|
|
17960
|
+
next.delete(SCORE_ID_PARAM);
|
|
17961
|
+
return next;
|
|
17962
|
+
},
|
|
17963
|
+
{ replace: true }
|
|
17964
|
+
);
|
|
17965
|
+
},
|
|
17966
|
+
[setSearchParams]
|
|
17967
|
+
);
|
|
17968
|
+
const handleTraceClose = React.useCallback(() => handleTraceClick(""), [handleTraceClick]);
|
|
17969
|
+
const handleSpanChange = React.useCallback(
|
|
17970
|
+
(spanId) => {
|
|
17971
|
+
const currentSpanId = searchParams.get(SPAN_ID_PARAM) || null;
|
|
17972
|
+
if (spanId === currentSpanId) return;
|
|
17973
|
+
setSearchParams(
|
|
17974
|
+
(prev) => {
|
|
17975
|
+
const next = new URLSearchParams(prev);
|
|
17976
|
+
if (spanId) {
|
|
17977
|
+
next.set(SPAN_ID_PARAM, spanId);
|
|
17978
|
+
} else {
|
|
17979
|
+
next.delete(SPAN_ID_PARAM);
|
|
17980
|
+
}
|
|
17981
|
+
next.delete(TAB_PARAM);
|
|
17982
|
+
next.delete(SCORE_ID_PARAM);
|
|
17983
|
+
return next;
|
|
17984
|
+
},
|
|
17985
|
+
{ replace: true }
|
|
17986
|
+
);
|
|
17987
|
+
},
|
|
17988
|
+
[searchParams, setSearchParams]
|
|
17989
|
+
);
|
|
17990
|
+
const handleSpanClose = React.useCallback(() => handleSpanChange(null), [handleSpanChange]);
|
|
17991
|
+
const handleSpanTabChange = React.useCallback(
|
|
17992
|
+
(tab) => {
|
|
17993
|
+
const currentTab = searchParams.get(TAB_PARAM) || null;
|
|
17994
|
+
if (tab === currentTab) return;
|
|
17995
|
+
setSearchParams(
|
|
17996
|
+
(prev) => {
|
|
17997
|
+
const next = new URLSearchParams(prev);
|
|
17998
|
+
if (tab && tab !== "details") {
|
|
17999
|
+
next.set(TAB_PARAM, tab);
|
|
18000
|
+
} else {
|
|
18001
|
+
next.delete(TAB_PARAM);
|
|
18002
|
+
}
|
|
18003
|
+
next.delete(SCORE_ID_PARAM);
|
|
18004
|
+
return next;
|
|
18005
|
+
},
|
|
18006
|
+
{ replace: true }
|
|
18007
|
+
);
|
|
18008
|
+
},
|
|
18009
|
+
[searchParams, setSearchParams]
|
|
18010
|
+
);
|
|
18011
|
+
const handleScoreChange = React.useCallback(
|
|
18012
|
+
(scoreId) => {
|
|
18013
|
+
const currentScoreId = searchParams.get(SCORE_ID_PARAM) || null;
|
|
18014
|
+
if (scoreId === currentScoreId) return;
|
|
18015
|
+
setSearchParams(
|
|
18016
|
+
(prev) => {
|
|
18017
|
+
const next = new URLSearchParams(prev);
|
|
18018
|
+
if (scoreId) {
|
|
18019
|
+
next.set(SCORE_ID_PARAM, scoreId);
|
|
18020
|
+
} else {
|
|
18021
|
+
next.delete(SCORE_ID_PARAM);
|
|
18022
|
+
}
|
|
18023
|
+
return next;
|
|
18024
|
+
},
|
|
18025
|
+
{ replace: true }
|
|
18026
|
+
);
|
|
18027
|
+
},
|
|
18028
|
+
[searchParams, setSearchParams]
|
|
18029
|
+
);
|
|
18030
|
+
const applyFilterTokens = React.useCallback(
|
|
18031
|
+
(tokens) => {
|
|
18032
|
+
setSearchParams(
|
|
18033
|
+
(prev) => {
|
|
18034
|
+
const next = new URLSearchParams(prev);
|
|
18035
|
+
applyTracePropertyFilterTokens(next, tokens);
|
|
18036
|
+
clearSelectionParams$1(next);
|
|
18037
|
+
return next;
|
|
18038
|
+
},
|
|
18039
|
+
{ replace: true }
|
|
18040
|
+
);
|
|
18041
|
+
},
|
|
18042
|
+
[setSearchParams]
|
|
18043
|
+
);
|
|
18044
|
+
const handleFilterTokensChange = applyFilterTokens;
|
|
18045
|
+
const handleDateChange = React.useCallback(
|
|
18046
|
+
(value, type) => {
|
|
18047
|
+
if (datePresetRef.current !== "custom") return;
|
|
18048
|
+
const param = type === "from" ? TRACE_DATE_FROM_PARAM : TRACE_DATE_TO_PARAM;
|
|
18049
|
+
setSearchParams(
|
|
18050
|
+
(prev) => {
|
|
18051
|
+
const next = new URLSearchParams(prev);
|
|
18052
|
+
if (value) {
|
|
18053
|
+
next.set(param, value.toISOString());
|
|
18054
|
+
} else {
|
|
18055
|
+
next.delete(param);
|
|
18056
|
+
}
|
|
18057
|
+
clearSelectionParams$1(next);
|
|
18058
|
+
return next;
|
|
18059
|
+
},
|
|
18060
|
+
{ replace: true }
|
|
18061
|
+
);
|
|
18062
|
+
},
|
|
18063
|
+
[setSearchParams]
|
|
18064
|
+
);
|
|
18065
|
+
const handleDatePresetChange = React.useCallback(
|
|
18066
|
+
(preset) => {
|
|
18067
|
+
datePresetRef.current = preset;
|
|
18068
|
+
setSearchParams(
|
|
18069
|
+
(prev) => {
|
|
18070
|
+
const next = new URLSearchParams(prev);
|
|
18071
|
+
if (preset === "last-24h") {
|
|
18072
|
+
next.delete(TRACE_DATE_PRESET_PARAM);
|
|
18073
|
+
next.delete(TRACE_DATE_FROM_PARAM);
|
|
18074
|
+
next.delete(TRACE_DATE_TO_PARAM);
|
|
18075
|
+
} else if (preset === "custom") {
|
|
18076
|
+
next.set(TRACE_DATE_PRESET_PARAM, "custom");
|
|
18077
|
+
} else {
|
|
18078
|
+
next.set(TRACE_DATE_PRESET_PARAM, preset);
|
|
18079
|
+
next.delete(TRACE_DATE_FROM_PARAM);
|
|
18080
|
+
next.delete(TRACE_DATE_TO_PARAM);
|
|
18081
|
+
}
|
|
18082
|
+
clearSelectionParams$1(next);
|
|
18083
|
+
return next;
|
|
18084
|
+
},
|
|
18085
|
+
{ replace: true }
|
|
18086
|
+
);
|
|
18087
|
+
},
|
|
18088
|
+
[setSearchParams]
|
|
18089
|
+
);
|
|
18090
|
+
const handleRemoveAll = React.useCallback(() => {
|
|
18091
|
+
setSearchParams(
|
|
18092
|
+
(prev) => {
|
|
18093
|
+
const next = new URLSearchParams(prev);
|
|
18094
|
+
next.delete(TRACE_ROOT_ENTITY_TYPE_PARAM);
|
|
18095
|
+
next.delete(TRACE_STATUS_PARAM);
|
|
18096
|
+
for (const fieldId of TRACE_PROPERTY_FILTER_FIELD_IDS) {
|
|
18097
|
+
next.delete(TRACE_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId]);
|
|
18098
|
+
}
|
|
18099
|
+
clearSelectionParams$1(next);
|
|
18100
|
+
return next;
|
|
18101
|
+
},
|
|
18102
|
+
{ replace: true }
|
|
18103
|
+
);
|
|
18104
|
+
onRemoveAll?.();
|
|
18105
|
+
}, [setSearchParams, onRemoveAll]);
|
|
18106
|
+
return {
|
|
18107
|
+
searchParams,
|
|
18108
|
+
setSearchParams,
|
|
18109
|
+
datePreset,
|
|
18110
|
+
selectedDateFrom,
|
|
18111
|
+
selectedDateTo,
|
|
18112
|
+
datePresetRef,
|
|
18113
|
+
traceIdParam,
|
|
18114
|
+
spanIdParam,
|
|
18115
|
+
spanTabParam,
|
|
18116
|
+
scoreIdParam,
|
|
18117
|
+
selectedEntityOption,
|
|
18118
|
+
selectedStatus,
|
|
18119
|
+
filterTokens,
|
|
18120
|
+
handleTraceClick,
|
|
18121
|
+
handleTraceClose,
|
|
18122
|
+
handleSpanChange,
|
|
18123
|
+
handleSpanClose,
|
|
18124
|
+
handleSpanTabChange,
|
|
18125
|
+
handleScoreChange,
|
|
18126
|
+
handleFilterTokensChange,
|
|
18127
|
+
handleDateChange,
|
|
18128
|
+
handleDatePresetChange,
|
|
18129
|
+
handleRemoveAll,
|
|
18130
|
+
applyFilterTokens
|
|
18131
|
+
};
|
|
18132
|
+
}
|
|
18133
|
+
|
|
18134
|
+
const DEFAULT_SAVED_MESSAGE$1 = "Trace filter settings saved.";
|
|
18135
|
+
const DEFAULT_CLEARED_MESSAGE$1 = "Trace filter settings cleared.";
|
|
18136
|
+
function useTraceFilterPersistence(searchParams, setSearchParams, options) {
|
|
18137
|
+
const { storageKey, messages, skipHydration } = options ?? {};
|
|
18138
|
+
const [hasSavedFilters, setHasSavedFilters] = React.useState(() => loadTraceFiltersFromStorage(storageKey) !== null);
|
|
18139
|
+
const handleSave = React.useCallback(() => {
|
|
18140
|
+
saveTraceFiltersToStorage(searchParams, storageKey);
|
|
18141
|
+
setHasSavedFilters(true);
|
|
18142
|
+
const text = messages?.saved ?? DEFAULT_SAVED_MESSAGE$1;
|
|
18143
|
+
if (text !== false) toast.success(text);
|
|
18144
|
+
}, [searchParams, storageKey, messages?.saved]);
|
|
18145
|
+
const handleRemoveSaved = React.useCallback(() => {
|
|
18146
|
+
clearSavedTraceFilters(storageKey);
|
|
18147
|
+
setHasSavedFilters(false);
|
|
18148
|
+
const text = messages?.cleared ?? DEFAULT_CLEARED_MESSAGE$1;
|
|
18149
|
+
if (text !== false) toast.success(text);
|
|
18150
|
+
}, [storageKey, messages?.cleared]);
|
|
18151
|
+
const hydratedRef = React.useRef(false);
|
|
18152
|
+
React.useEffect(() => {
|
|
18153
|
+
if (skipHydration) return;
|
|
18154
|
+
if (hydratedRef.current) return;
|
|
18155
|
+
hydratedRef.current = true;
|
|
18156
|
+
if (hasAnyTraceFilterParams(searchParams)) return;
|
|
18157
|
+
const saved = loadTraceFiltersFromStorage(storageKey);
|
|
18158
|
+
if (!saved) return;
|
|
18159
|
+
setSearchParams(
|
|
18160
|
+
(prev) => {
|
|
18161
|
+
const next = new URLSearchParams(prev);
|
|
18162
|
+
for (const [key, value] of saved) {
|
|
18163
|
+
next.append(key, value);
|
|
18164
|
+
}
|
|
18165
|
+
return next;
|
|
18166
|
+
},
|
|
18167
|
+
{ replace: true }
|
|
18168
|
+
);
|
|
18169
|
+
}, []);
|
|
18170
|
+
return { hasSavedFilters, handleSave, handleRemoveSaved };
|
|
18171
|
+
}
|
|
18172
|
+
|
|
18173
|
+
const CONTEXT_FIELD_IDS = [
|
|
18174
|
+
"environment",
|
|
18175
|
+
"serviceName",
|
|
18176
|
+
"source",
|
|
18177
|
+
"scope",
|
|
18178
|
+
"userId",
|
|
18179
|
+
"organizationId",
|
|
18180
|
+
"resourceId",
|
|
18181
|
+
"runId",
|
|
18182
|
+
"sessionId",
|
|
18183
|
+
"threadId",
|
|
18184
|
+
"requestId",
|
|
18185
|
+
"experimentId",
|
|
18186
|
+
"spanType",
|
|
18187
|
+
"entityName",
|
|
18188
|
+
"parentEntityType",
|
|
18189
|
+
"parentEntityId",
|
|
18190
|
+
"parentEntityName",
|
|
18191
|
+
"rootEntityType",
|
|
18192
|
+
"rootEntityId",
|
|
18193
|
+
"rootEntityName"
|
|
18194
|
+
];
|
|
18195
|
+
|
|
18196
|
+
const KV = DataDetailsPanel.KeyValueList;
|
|
18197
|
+
function toDate(value) {
|
|
18198
|
+
return value instanceof Date ? value : new Date(value);
|
|
18199
|
+
}
|
|
18200
|
+
function LogDetailsView({
|
|
18201
|
+
log,
|
|
18202
|
+
onClose,
|
|
18203
|
+
onTraceClick,
|
|
18204
|
+
onSpanClick,
|
|
18205
|
+
onPrevious,
|
|
18206
|
+
onNext,
|
|
18207
|
+
collapsed: controlledCollapsed,
|
|
18208
|
+
onCollapsedChange
|
|
18209
|
+
}) {
|
|
18210
|
+
const [internalCollapsed, setInternalCollapsed] = React.useState(false);
|
|
18211
|
+
const collapsed = controlledCollapsed ?? internalCollapsed;
|
|
18212
|
+
const setCollapsed = onCollapsedChange ?? setInternalCollapsed;
|
|
18213
|
+
const date = toDate(log.timestamp);
|
|
18214
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel, { collapsed, children: [
|
|
18215
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Header, { children: [
|
|
18216
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Heading, { children: [
|
|
18217
|
+
"Log ",
|
|
18218
|
+
/* @__PURE__ */ jsxRuntime.jsx("b", { children: dateFns.format(date, "MMM dd, HH:mm:ss.SSS") })
|
|
18219
|
+
] }),
|
|
18220
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { className: "ml-auto shrink-0", children: [
|
|
18221
|
+
onCollapsedChange && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18222
|
+
ButtonWithTooltip,
|
|
18223
|
+
{
|
|
18224
|
+
size: "md",
|
|
18225
|
+
tooltipContent: collapsed ? "Expand panel" : "Collapse panel",
|
|
18226
|
+
onClick: () => setCollapsed(!collapsed),
|
|
18227
|
+
children: collapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDownIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsDownUpIcon, {})
|
|
18228
|
+
}
|
|
18229
|
+
),
|
|
18230
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { spacing: "close", children: [
|
|
18231
|
+
/* @__PURE__ */ jsxRuntime.jsx(ButtonWithTooltip, { size: "md", tooltipContent: "Previous log", onClick: onPrevious, disabled: !onPrevious, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpIcon, {}) }),
|
|
18232
|
+
/* @__PURE__ */ jsxRuntime.jsx(ButtonWithTooltip, { size: "md", tooltipContent: "Next log", onClick: onNext, disabled: !onNext, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownIcon, {}) })
|
|
18233
|
+
] }),
|
|
18234
|
+
/* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.CloseButton, { onClick: onClose })
|
|
18235
|
+
] })
|
|
18236
|
+
] }),
|
|
18237
|
+
!collapsed && /* @__PURE__ */ jsxRuntime.jsxs(DataDetailsPanel.Content, { children: [
|
|
18238
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-ui-md text-neutral4 font-mono wrap-break-word whitespace-pre-wrap", children: log.message }),
|
|
18239
|
+
(log.traceId || log.spanId) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid gap-2 my-8", "[&>button]:justify-between [&>button]:overflow-hidden"), children: [
|
|
18240
|
+
log.traceId && /* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { spacing: "close", className: "min-w-0 w-full", children: [
|
|
18241
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
18242
|
+
Button,
|
|
18243
|
+
{
|
|
18244
|
+
size: "md",
|
|
18245
|
+
className: "min-w-0 flex-1 overflow-hidden",
|
|
18246
|
+
onClick: () => onTraceClick?.(log.traceId),
|
|
18247
|
+
children: [
|
|
18248
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightIcon, {}),
|
|
18249
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Trace" }),
|
|
18250
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: " ml-auto text-ui-sm text-neutral2 min-w-0 truncate", children: [
|
|
18251
|
+
"# ",
|
|
18252
|
+
log.traceId
|
|
18253
|
+
] })
|
|
18254
|
+
]
|
|
18255
|
+
}
|
|
18256
|
+
),
|
|
18257
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyButton, { content: log.traceId, size: "md", tooltip: "Copy Trace ID to clipboard" })
|
|
18258
|
+
] }),
|
|
18259
|
+
log.spanId && /* @__PURE__ */ jsxRuntime.jsxs(ButtonsGroup, { spacing: "close", className: "min-w-0 w-full", children: [
|
|
18260
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
18261
|
+
Button,
|
|
18262
|
+
{
|
|
18263
|
+
size: "md",
|
|
18264
|
+
className: "min-w-0 flex-1 overflow-hidden",
|
|
18265
|
+
disabled: !log.traceId || !onSpanClick,
|
|
18266
|
+
onClick: () => log.traceId && onSpanClick?.(log.traceId, log.spanId),
|
|
18267
|
+
children: [
|
|
18268
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowRightIcon, {}),
|
|
18269
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Span" }),
|
|
18270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: " ml-auto text-ui-sm text-neutral2 min-w-0 truncate", children: [
|
|
18271
|
+
"# ",
|
|
18272
|
+
log.spanId
|
|
18273
|
+
] })
|
|
18274
|
+
]
|
|
18275
|
+
}
|
|
18276
|
+
),
|
|
18277
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyButton, { content: log.spanId, size: "md", tooltip: "Copy Span ID to clipboard" })
|
|
18278
|
+
] })
|
|
18279
|
+
] }),
|
|
18280
|
+
/* @__PURE__ */ jsxRuntime.jsxs(KV, { className: "mb-6", children: [
|
|
18281
|
+
log.entityType && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18282
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: "Entity Type" }),
|
|
18283
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: log.entityType })
|
|
18284
|
+
] }),
|
|
18285
|
+
log.entityName && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18286
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: "Entity Name" }),
|
|
18287
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: log.entityName })
|
|
18288
|
+
] }),
|
|
18289
|
+
log.serviceName && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18290
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: "Service" }),
|
|
18291
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: log.serviceName })
|
|
18292
|
+
] }),
|
|
18293
|
+
log.environment && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18294
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: "Environment" }),
|
|
18295
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: log.environment })
|
|
18296
|
+
] }),
|
|
18297
|
+
log.source && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18298
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: "Source" }),
|
|
18299
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: log.source })
|
|
18300
|
+
] }),
|
|
18301
|
+
log.metadata && Object.keys(log.metadata).length > 0 && /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: Object.entries(log.metadata).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs(React.Fragment, { children: [
|
|
18302
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Key, { children: key }),
|
|
18303
|
+
/* @__PURE__ */ jsxRuntime.jsx(KV.Value, { children: String(value) })
|
|
18304
|
+
] }, key)) })
|
|
18305
|
+
] }),
|
|
18306
|
+
log.data && Object.keys(log.data).length > 0 && /* @__PURE__ */ jsxRuntime.jsx(DataDetailsPanel.CodeSection, { title: "Data", codeStr: JSON.stringify(log.data, null, 2), className: "mt-6" })
|
|
18307
|
+
] })
|
|
18308
|
+
] });
|
|
18309
|
+
}
|
|
18310
|
+
|
|
18311
|
+
function LogsErrorContent({ error, resource, errorTitle }) {
|
|
18312
|
+
if (is401UnauthorizedError(error)) return /* @__PURE__ */ jsxRuntime.jsx(SessionExpired, {});
|
|
18313
|
+
if (is403ForbiddenError(error)) return /* @__PURE__ */ jsxRuntime.jsx(PermissionDenied, { resource });
|
|
18314
|
+
const message = error instanceof Error ? error.message : void 0;
|
|
18315
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ErrorState, { title: errorTitle, message: message ?? "Unknown error" });
|
|
18316
|
+
}
|
|
18317
|
+
|
|
18318
|
+
function LogsLayout({ listSlot, logPanelSlot, tracePanelSlot, spanPanelSlot, logCollapsed }) {
|
|
18319
|
+
const hasSidePanel = !!logPanelSlot;
|
|
18320
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18321
|
+
"div",
|
|
18322
|
+
{
|
|
18323
|
+
className: cn("grid h-full min-h-0 gap-4 items-start", hasSidePanel ? "grid-cols-[1fr_1fr]" : "grid-cols-[1fr]"),
|
|
18324
|
+
children: [
|
|
18325
|
+
listSlot,
|
|
18326
|
+
hasSidePanel && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18327
|
+
"div",
|
|
18328
|
+
{
|
|
18329
|
+
className: cn(
|
|
18330
|
+
"grid gap-4 h-full overflow-auto",
|
|
18331
|
+
tracePanelSlot && spanPanelSlot ? logCollapsed ? "grid-rows-[auto_1fr_1fr]" : "grid-rows-[1fr_1fr_1fr]" : tracePanelSlot ? logCollapsed ? "grid-rows-[auto_1fr]" : "grid-rows-[1fr_1fr]" : logCollapsed ? "grid-rows-[auto]" : "grid-rows-[1fr]"
|
|
18332
|
+
),
|
|
18333
|
+
children: [
|
|
18334
|
+
logPanelSlot,
|
|
18335
|
+
tracePanelSlot,
|
|
18336
|
+
spanPanelSlot
|
|
18337
|
+
]
|
|
18338
|
+
}
|
|
18339
|
+
)
|
|
18340
|
+
]
|
|
18341
|
+
}
|
|
18342
|
+
);
|
|
18343
|
+
}
|
|
18344
|
+
|
|
18345
|
+
const COLUMNS = "auto auto auto auto minmax(5rem,1fr) minmax(5rem,1fr)";
|
|
18346
|
+
function LogsListView({
|
|
18347
|
+
logs,
|
|
18348
|
+
isLoading,
|
|
18349
|
+
isFetchingNextPage,
|
|
18350
|
+
hasNextPage,
|
|
18351
|
+
setEndOfListElement,
|
|
18352
|
+
logIdMap,
|
|
18353
|
+
featuredLogId,
|
|
18354
|
+
onLogClick
|
|
18355
|
+
}) {
|
|
18356
|
+
if (isLoading) {
|
|
18357
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataListSkeleton, { columns: COLUMNS });
|
|
18358
|
+
}
|
|
18359
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(LogsDataList, { columns: COLUMNS, className: "min-w-0", children: [
|
|
18360
|
+
/* @__PURE__ */ jsxRuntime.jsxs(LogsDataList.Top, { children: [
|
|
18361
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Date" }),
|
|
18362
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Time" }),
|
|
18363
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Level" }),
|
|
18364
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Entity" }),
|
|
18365
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Message" }),
|
|
18366
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TopCell, { children: "Data" })
|
|
18367
|
+
] }),
|
|
18368
|
+
logs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(LogsDataList.NoMatch, { message: "No logs match your search" }) : logs.map((log) => {
|
|
18369
|
+
const id = logIdMap.get(log);
|
|
18370
|
+
if (!id) return null;
|
|
18371
|
+
const isFeatured = id === featuredLogId;
|
|
18372
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
18373
|
+
LogsDataList.RowButton,
|
|
18374
|
+
{
|
|
18375
|
+
onClick: () => onLogClick(log),
|
|
18376
|
+
className: cn(isFeatured && "bg-surface4"),
|
|
18377
|
+
children: [
|
|
18378
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.DateCell, { timestamp: log.timestamp }),
|
|
18379
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.TimeCell, { timestamp: log.timestamp }),
|
|
18380
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.LevelCell, { level: log.level }),
|
|
18381
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.EntityCell, { entityType: log.entityType, entityName: log.entityName }),
|
|
18382
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.MessageCell, { message: log.message }),
|
|
18383
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogsDataList.DataCell, { data: log.data })
|
|
18384
|
+
]
|
|
18385
|
+
},
|
|
18386
|
+
id
|
|
18387
|
+
);
|
|
18388
|
+
}),
|
|
18389
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18390
|
+
LogsDataList.NextPageLoading,
|
|
18391
|
+
{
|
|
18392
|
+
isLoading: isFetchingNextPage,
|
|
18393
|
+
hasMore: hasNextPage,
|
|
18394
|
+
setEndOfListElement
|
|
18395
|
+
}
|
|
18396
|
+
)
|
|
18397
|
+
] });
|
|
18398
|
+
}
|
|
18399
|
+
|
|
18400
|
+
function LogsToolbar({
|
|
18401
|
+
onClear,
|
|
18402
|
+
onRemoveAll,
|
|
18403
|
+
onSave,
|
|
18404
|
+
onRemoveSaved,
|
|
18405
|
+
isLoading,
|
|
18406
|
+
filterFields,
|
|
18407
|
+
filterTokens,
|
|
18408
|
+
onFilterTokensChange,
|
|
18409
|
+
autoFocusFilterFieldId
|
|
18410
|
+
}) {
|
|
18411
|
+
const hasActiveFilters = filterTokens.length > 0;
|
|
18412
|
+
const hasNonDefaultFilter = filterTokens.some((token) => isNonDefaultFilter(token, filterFields));
|
|
18413
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid grid-cols-[1fr_auto] gap-3 items-start"), children: [
|
|
18414
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18415
|
+
PropertyFilterApplied,
|
|
18416
|
+
{
|
|
18417
|
+
fields: filterFields,
|
|
18418
|
+
tokens: filterTokens,
|
|
18419
|
+
onTokensChange: onFilterTokensChange,
|
|
18420
|
+
disabled: isLoading,
|
|
18421
|
+
autoFocusFieldId: autoFocusFilterFieldId
|
|
18422
|
+
}
|
|
18423
|
+
),
|
|
18424
|
+
hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
|
|
18425
|
+
PropertyFilterActions,
|
|
18426
|
+
{
|
|
18427
|
+
disabled: isLoading,
|
|
18428
|
+
onClear: hasNonDefaultFilter ? onClear : void 0,
|
|
18429
|
+
onRemoveAll,
|
|
18430
|
+
onSave,
|
|
18431
|
+
onRemoveSaved
|
|
18432
|
+
}
|
|
18433
|
+
)
|
|
18434
|
+
] });
|
|
18435
|
+
}
|
|
18436
|
+
function isNonDefaultFilter(token, fields) {
|
|
18437
|
+
const field = fields.find((f) => f.id === token.fieldId);
|
|
18438
|
+
if (!field) return false;
|
|
18439
|
+
if (field.kind === "text") {
|
|
18440
|
+
return typeof token.value === "string" && token.value.trim() !== "";
|
|
18441
|
+
}
|
|
18442
|
+
if (field.kind === "pick-multi") {
|
|
18443
|
+
if (field.multi) return Array.isArray(token.value) && token.value.length > 0;
|
|
18444
|
+
return typeof token.value === "string" && token.value !== "" && token.value !== "Any";
|
|
18445
|
+
}
|
|
18446
|
+
if (field.kind === "multi-select") {
|
|
18447
|
+
return Array.isArray(token.value) && token.value.length > 0;
|
|
18448
|
+
}
|
|
18449
|
+
return false;
|
|
18450
|
+
}
|
|
18451
|
+
|
|
18452
|
+
const NoLogsInfo = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
18453
|
+
EmptyState,
|
|
18454
|
+
{
|
|
18455
|
+
iconSlot: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleSlashIcon, {}),
|
|
18456
|
+
titleSlot: "No logs yet",
|
|
18457
|
+
descriptionSlot: "Logs will appear here once agents, workflows, or tools are executed."
|
|
18458
|
+
}
|
|
18459
|
+
) });
|
|
18460
|
+
|
|
18461
|
+
const LOGS_PER_PAGE = 20;
|
|
18462
|
+
function getNextPageParam(lastPage, _allPages, lastPageParam) {
|
|
18463
|
+
if (lastPage?.pagination?.hasMore) {
|
|
18464
|
+
return lastPageParam + 1;
|
|
18465
|
+
}
|
|
18466
|
+
return void 0;
|
|
18467
|
+
}
|
|
18468
|
+
function selectLogs(data) {
|
|
18469
|
+
return data.pages.flatMap((page) => page.logs ?? []);
|
|
18470
|
+
}
|
|
18471
|
+
const useLogs = ({ filters } = {}) => {
|
|
18472
|
+
const client = react.useMastraClient();
|
|
18473
|
+
const { inView: isEndOfListInView, setRef: setEndOfListElement } = useInView();
|
|
18474
|
+
const query = reactQuery.useInfiniteQuery({
|
|
18475
|
+
queryKey: ["logs", filters],
|
|
18476
|
+
queryFn: ({ pageParam }) => client.listLogsVNext({
|
|
18477
|
+
pagination: { page: pageParam, perPage: LOGS_PER_PAGE },
|
|
18478
|
+
filters,
|
|
18479
|
+
orderBy: { field: "timestamp", direction: "DESC" }
|
|
18480
|
+
}),
|
|
18481
|
+
initialPageParam: 0,
|
|
18482
|
+
getNextPageParam,
|
|
18483
|
+
select: selectLogs,
|
|
18484
|
+
retry: false,
|
|
18485
|
+
refetchInterval: 3e3
|
|
18486
|
+
});
|
|
18487
|
+
const { hasNextPage, isFetchingNextPage, fetchNextPage } = query;
|
|
18488
|
+
React.useEffect(() => {
|
|
18489
|
+
if (isEndOfListInView && hasNextPage && !isFetchingNextPage) {
|
|
18490
|
+
void fetchNextPage();
|
|
18491
|
+
}
|
|
18492
|
+
}, [isEndOfListInView, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
18493
|
+
return { ...query, data: query.data, setEndOfListElement };
|
|
18494
|
+
};
|
|
18495
|
+
|
|
18496
|
+
const LOGS_ROOT_ENTITY_TYPES = {
|
|
18497
|
+
AGENT: observability.EntityType.AGENT,
|
|
18498
|
+
WORKFLOW: observability.EntityType.WORKFLOW_RUN,
|
|
18499
|
+
SCORER: observability.EntityType.SCORER,
|
|
18500
|
+
INGEST: observability.EntityType.RAG_INGESTION
|
|
18501
|
+
};
|
|
18502
|
+
const LOGS_ROOT_ENTITY_TYPE_OPTIONS = [
|
|
18503
|
+
{ label: "Agent", entityType: LOGS_ROOT_ENTITY_TYPES.AGENT },
|
|
18504
|
+
{ label: "Workflow", entityType: LOGS_ROOT_ENTITY_TYPES.WORKFLOW },
|
|
18505
|
+
{ label: "Scorer", entityType: LOGS_ROOT_ENTITY_TYPES.SCORER },
|
|
18506
|
+
{ label: "Ingest", entityType: LOGS_ROOT_ENTITY_TYPES.INGEST }
|
|
18507
|
+
];
|
|
18508
|
+
const LOG_LEVEL_VALUES = ["debug", "info", "warn", "error", "fatal"];
|
|
18509
|
+
const LOG_LEVEL_OPTIONS = [
|
|
18510
|
+
{ label: "Debug", value: "debug" },
|
|
18511
|
+
{ label: "Info", value: "info" },
|
|
18512
|
+
{ label: "Warn", value: "warn" },
|
|
18513
|
+
{ label: "Error", value: "error" },
|
|
18514
|
+
{ label: "Fatal", value: "fatal" }
|
|
18515
|
+
];
|
|
18516
|
+
const LOGS_ROOT_ENTITY_TYPE_PARAM = "rootEntityType";
|
|
18517
|
+
const LOGS_DATE_PRESET_PARAM = "datePreset";
|
|
18518
|
+
const LOGS_DATE_FROM_PARAM = "dateFrom";
|
|
18519
|
+
const LOGS_DATE_TO_PARAM = "dateTo";
|
|
18520
|
+
const LOGS_DATE_PRESET_VALUES = /* @__PURE__ */ new Set([
|
|
18521
|
+
"all",
|
|
18522
|
+
"last-24h",
|
|
18523
|
+
"last-3d",
|
|
18524
|
+
"last-7d",
|
|
18525
|
+
"last-14d",
|
|
18526
|
+
"last-30d",
|
|
18527
|
+
"custom"
|
|
18528
|
+
]);
|
|
18529
|
+
const LOGS_PROPERTY_FILTER_PARAM_BY_FIELD = {
|
|
18530
|
+
level: "filterLevel",
|
|
18531
|
+
tags: "filterTags",
|
|
18532
|
+
entityName: "filterEntityName",
|
|
18533
|
+
traceId: "filterTraceId",
|
|
18534
|
+
spanId: "filterSpanId",
|
|
18535
|
+
runId: "filterRunId",
|
|
18536
|
+
threadId: "filterThreadId",
|
|
18537
|
+
sessionId: "filterSessionId",
|
|
18538
|
+
requestId: "filterRequestId",
|
|
18539
|
+
resourceId: "filterResourceId",
|
|
18540
|
+
userId: "filterUserId",
|
|
18541
|
+
organizationId: "filterOrganizationId",
|
|
18542
|
+
serviceName: "filterServiceName",
|
|
18543
|
+
environment: "filterEnvironment",
|
|
18544
|
+
experimentId: "filterExperimentId"
|
|
18545
|
+
};
|
|
18546
|
+
const LOGS_PROPERTY_FILTER_FIELD_IDS = Object.keys(LOGS_PROPERTY_FILTER_PARAM_BY_FIELD);
|
|
18547
|
+
const LOGS_ARRAY_FIELD_IDS = /* @__PURE__ */ new Set(["tags"]);
|
|
18548
|
+
const DEFAULT_LOGS_FILTERS_STORAGE_KEY = "mastra:logs:saved-filters";
|
|
18549
|
+
function saveLogsFiltersToStorage(params, storageKey = DEFAULT_LOGS_FILTERS_STORAGE_KEY) {
|
|
18550
|
+
const serialized = getPreservedLogsFilterParams(params);
|
|
18551
|
+
const preset = params.get(LOGS_DATE_PRESET_PARAM);
|
|
18552
|
+
if (preset) serialized.set(LOGS_DATE_PRESET_PARAM, preset);
|
|
18553
|
+
const from = params.get(LOGS_DATE_FROM_PARAM);
|
|
18554
|
+
if (from) serialized.set(LOGS_DATE_FROM_PARAM, from);
|
|
18555
|
+
const to = params.get(LOGS_DATE_TO_PARAM);
|
|
18556
|
+
if (to) serialized.set(LOGS_DATE_TO_PARAM, to);
|
|
18557
|
+
try {
|
|
18558
|
+
localStorage.setItem(storageKey, serialized.toString());
|
|
18559
|
+
} catch {
|
|
18560
|
+
}
|
|
18561
|
+
}
|
|
18562
|
+
function clearSavedLogsFilters(storageKey = DEFAULT_LOGS_FILTERS_STORAGE_KEY) {
|
|
18563
|
+
try {
|
|
18564
|
+
localStorage.removeItem(storageKey);
|
|
18565
|
+
} catch {
|
|
18566
|
+
}
|
|
18567
|
+
}
|
|
18568
|
+
function loadLogsFiltersFromStorage(storageKey = DEFAULT_LOGS_FILTERS_STORAGE_KEY) {
|
|
18569
|
+
try {
|
|
18570
|
+
const raw = localStorage.getItem(storageKey);
|
|
18571
|
+
if (!raw) return null;
|
|
18572
|
+
const parsed = new URLSearchParams(raw);
|
|
18573
|
+
return parsed.toString() ? parsed : null;
|
|
18574
|
+
} catch {
|
|
18575
|
+
return null;
|
|
18576
|
+
}
|
|
18577
|
+
}
|
|
18578
|
+
function hasAnyLogsFilterParams(params) {
|
|
18579
|
+
if (params.has(LOGS_DATE_PRESET_PARAM)) return true;
|
|
18580
|
+
if (params.has(LOGS_DATE_FROM_PARAM)) return true;
|
|
18581
|
+
if (params.has(LOGS_DATE_TO_PARAM)) return true;
|
|
18582
|
+
if (params.has(LOGS_ROOT_ENTITY_TYPE_PARAM)) return true;
|
|
18583
|
+
for (const fieldId of LOGS_PROPERTY_FILTER_FIELD_IDS) {
|
|
18584
|
+
if (params.has(LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId])) return true;
|
|
18585
|
+
}
|
|
18586
|
+
return false;
|
|
18587
|
+
}
|
|
18588
|
+
function createLogsPropertyFilterFields({
|
|
18589
|
+
availableTags,
|
|
18590
|
+
availableRootEntityNames,
|
|
18591
|
+
availableServiceNames,
|
|
18592
|
+
availableEnvironments,
|
|
18593
|
+
loading
|
|
18594
|
+
}) {
|
|
18595
|
+
const fields = [
|
|
18596
|
+
{
|
|
18597
|
+
id: "rootEntityType",
|
|
18598
|
+
label: "Primitive Type",
|
|
18599
|
+
kind: "pick-multi",
|
|
18600
|
+
searchable: false,
|
|
18601
|
+
options: LOGS_ROOT_ENTITY_TYPE_OPTIONS.map((o) => ({ label: o.label, value: o.entityType })),
|
|
18602
|
+
placeholder: "Choose entity type",
|
|
18603
|
+
emptyText: "No entity types."
|
|
18604
|
+
},
|
|
18605
|
+
{
|
|
18606
|
+
id: "entityName",
|
|
18607
|
+
label: "Primitive Name",
|
|
18608
|
+
kind: "pick-multi",
|
|
18609
|
+
options: availableRootEntityNames.map((name) => ({ label: name, value: name })),
|
|
18610
|
+
placeholder: "Choose entity names",
|
|
18611
|
+
emptyText: "No entity names found.",
|
|
18612
|
+
isLoading: loading?.entityNames
|
|
18613
|
+
},
|
|
18614
|
+
{
|
|
18615
|
+
id: "level",
|
|
18616
|
+
label: "Level",
|
|
18617
|
+
kind: "pick-multi",
|
|
18618
|
+
searchable: false,
|
|
18619
|
+
options: LOG_LEVEL_OPTIONS.map((o) => ({ label: o.label, value: o.value })),
|
|
18620
|
+
placeholder: "Choose level",
|
|
18621
|
+
emptyText: "No levels."
|
|
18622
|
+
},
|
|
18623
|
+
{
|
|
18624
|
+
id: "tags",
|
|
18625
|
+
label: "Tags",
|
|
18626
|
+
kind: "pick-multi",
|
|
18627
|
+
multi: true,
|
|
18628
|
+
options: availableTags.map((tag) => ({ label: tag, value: tag })),
|
|
18629
|
+
placeholder: "Choose tags",
|
|
18630
|
+
emptyText: "No tags found.",
|
|
18631
|
+
isLoading: loading?.tags
|
|
18632
|
+
},
|
|
18633
|
+
{
|
|
18634
|
+
id: "serviceName",
|
|
18635
|
+
label: "Service Name",
|
|
18636
|
+
kind: "pick-multi",
|
|
18637
|
+
options: availableServiceNames.map((name) => ({ label: name, value: name })),
|
|
18638
|
+
placeholder: "Choose service names",
|
|
18639
|
+
emptyText: "No service names found.",
|
|
18640
|
+
isLoading: loading?.serviceNames
|
|
18641
|
+
},
|
|
18642
|
+
{
|
|
18643
|
+
id: "environment",
|
|
18644
|
+
label: "Environment",
|
|
18645
|
+
kind: "pick-multi",
|
|
18646
|
+
options: availableEnvironments.map((env) => ({ label: env, value: env })),
|
|
18647
|
+
placeholder: "Choose environments",
|
|
18648
|
+
emptyText: "No environments found.",
|
|
18649
|
+
isLoading: loading?.environments
|
|
18650
|
+
},
|
|
18651
|
+
{ id: "traceId", label: "Trace ID", kind: "text" },
|
|
18652
|
+
{ id: "spanId", label: "Span ID", kind: "text" },
|
|
18653
|
+
{ id: "runId", label: "Run ID", kind: "text" },
|
|
18654
|
+
{ id: "threadId", label: "Thread ID", kind: "text" },
|
|
18655
|
+
{ id: "sessionId", label: "Session ID", kind: "text" },
|
|
18656
|
+
{ id: "requestId", label: "Request ID", kind: "text" },
|
|
18657
|
+
{ id: "resourceId", label: "Resource ID", kind: "text" },
|
|
18658
|
+
{ id: "userId", label: "User ID", kind: "text" },
|
|
18659
|
+
{ id: "organizationId", label: "Organization ID", kind: "text" },
|
|
18660
|
+
{ id: "experimentId", label: "Experiment ID", kind: "text" }
|
|
18661
|
+
];
|
|
18662
|
+
const byLabel = (a, b) => a.label.localeCompare(b.label);
|
|
18663
|
+
const pickMulti = fields.filter((f) => f.kind === "pick-multi").sort(byLabel);
|
|
18664
|
+
const text = fields.filter((f) => f.kind === "text").sort(byLabel);
|
|
18665
|
+
return [...pickMulti, ...text];
|
|
18666
|
+
}
|
|
18667
|
+
function getLogsPropertyFilterTokens(searchParams) {
|
|
18668
|
+
const tokens = [];
|
|
18669
|
+
const paramToFieldId = /* @__PURE__ */ new Map([[LOGS_ROOT_ENTITY_TYPE_PARAM, "rootEntityType"]]);
|
|
18670
|
+
for (const fieldId of LOGS_PROPERTY_FILTER_FIELD_IDS) {
|
|
18671
|
+
paramToFieldId.set(LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId], fieldId);
|
|
18672
|
+
}
|
|
18673
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18674
|
+
for (const [paramName] of searchParams.entries()) {
|
|
18675
|
+
const fieldId = paramToFieldId.get(paramName);
|
|
18676
|
+
if (!fieldId || seen.has(fieldId)) continue;
|
|
18677
|
+
seen.add(fieldId);
|
|
18678
|
+
if (LOGS_ARRAY_FIELD_IDS.has(fieldId)) {
|
|
18679
|
+
const raw = searchParams.getAll(paramName);
|
|
18680
|
+
if (raw.length === 0) continue;
|
|
18681
|
+
tokens.push({ fieldId, value: raw.filter(Boolean) });
|
|
18682
|
+
continue;
|
|
18683
|
+
}
|
|
18684
|
+
const value = searchParams.get(paramName);
|
|
18685
|
+
if (value !== null) tokens.push({ fieldId, value });
|
|
18686
|
+
}
|
|
18687
|
+
return tokens;
|
|
18688
|
+
}
|
|
18689
|
+
function getPreservedLogsFilterParams(searchParams) {
|
|
18690
|
+
const next = new URLSearchParams();
|
|
18691
|
+
const rootEntityType = searchParams.get(LOGS_ROOT_ENTITY_TYPE_PARAM);
|
|
18692
|
+
if (rootEntityType) next.set(LOGS_ROOT_ENTITY_TYPE_PARAM, rootEntityType);
|
|
18693
|
+
for (const fieldId of LOGS_PROPERTY_FILTER_FIELD_IDS) {
|
|
18694
|
+
const param = LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId];
|
|
18695
|
+
if (LOGS_ARRAY_FIELD_IDS.has(fieldId)) {
|
|
18696
|
+
for (const value2 of searchParams.getAll(param)) {
|
|
18697
|
+
next.append(param, value2);
|
|
18698
|
+
}
|
|
18699
|
+
continue;
|
|
18700
|
+
}
|
|
18701
|
+
const value = searchParams.get(param);
|
|
18702
|
+
if (value) next.set(param, value);
|
|
18703
|
+
}
|
|
18704
|
+
return next;
|
|
18705
|
+
}
|
|
18706
|
+
function applyLogsPropertyFilterTokens(params, tokens) {
|
|
18707
|
+
params.delete(LOGS_ROOT_ENTITY_TYPE_PARAM);
|
|
18708
|
+
for (const fieldId of LOGS_PROPERTY_FILTER_FIELD_IDS) {
|
|
18709
|
+
params.delete(LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId]);
|
|
18710
|
+
}
|
|
18711
|
+
for (const token of tokens) {
|
|
18712
|
+
if (token.fieldId === "rootEntityType" && typeof token.value === "string") {
|
|
18713
|
+
params.set(LOGS_ROOT_ENTITY_TYPE_PARAM, token.value);
|
|
18714
|
+
continue;
|
|
18715
|
+
}
|
|
18716
|
+
const param = LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[token.fieldId];
|
|
18717
|
+
if (!param) continue;
|
|
18718
|
+
if (LOGS_ARRAY_FIELD_IDS.has(token.fieldId) && Array.isArray(token.value)) {
|
|
18719
|
+
if (token.value.length === 0) {
|
|
18720
|
+
params.append(param, "");
|
|
18721
|
+
} else {
|
|
18722
|
+
for (const value of token.value) {
|
|
18723
|
+
params.append(param, value);
|
|
18724
|
+
}
|
|
18725
|
+
}
|
|
18726
|
+
continue;
|
|
18727
|
+
}
|
|
18728
|
+
if (typeof token.value === "string") {
|
|
18729
|
+
params.set(param, token.value.trim());
|
|
18730
|
+
}
|
|
18731
|
+
}
|
|
18732
|
+
}
|
|
18733
|
+
function buildLogsListFilters({
|
|
18734
|
+
rootEntityType,
|
|
18735
|
+
dateFrom,
|
|
18736
|
+
dateTo,
|
|
18737
|
+
tokens
|
|
18738
|
+
}) {
|
|
18739
|
+
const filters = {};
|
|
18740
|
+
if (rootEntityType) {
|
|
18741
|
+
filters.rootEntityType = rootEntityType;
|
|
18742
|
+
}
|
|
18743
|
+
if (dateFrom || dateTo) {
|
|
18744
|
+
filters.timestamp = {
|
|
18745
|
+
...dateFrom ? { start: dateFrom } : {},
|
|
18746
|
+
...dateTo ? { end: dateTo } : {}
|
|
18747
|
+
};
|
|
18748
|
+
}
|
|
18749
|
+
for (const token of tokens) {
|
|
18750
|
+
if (token.fieldId === "tags") {
|
|
18751
|
+
if (Array.isArray(token.value) && token.value.length > 0) {
|
|
18752
|
+
filters.tags = token.value;
|
|
18753
|
+
} else if (typeof token.value === "string" && token.value.trim()) {
|
|
18754
|
+
filters.tags = [token.value.trim()];
|
|
18755
|
+
}
|
|
18756
|
+
continue;
|
|
18757
|
+
}
|
|
18758
|
+
if (token.fieldId === "level") {
|
|
18759
|
+
if (typeof token.value === "string" && token.value.trim() && token.value !== "Any") {
|
|
18760
|
+
filters.level = token.value;
|
|
18761
|
+
}
|
|
18762
|
+
continue;
|
|
18763
|
+
}
|
|
18764
|
+
if (typeof token.value !== "string") continue;
|
|
18765
|
+
if (!token.value.trim()) continue;
|
|
18766
|
+
if (token.value === "Any") continue;
|
|
18767
|
+
switch (token.fieldId) {
|
|
18768
|
+
case "entityName":
|
|
18769
|
+
filters.rootEntityName = token.value;
|
|
18770
|
+
break;
|
|
18771
|
+
case "traceId":
|
|
18772
|
+
filters.traceId = token.value;
|
|
18773
|
+
break;
|
|
18774
|
+
case "spanId":
|
|
18775
|
+
filters.spanId = token.value;
|
|
18776
|
+
break;
|
|
18777
|
+
case "runId":
|
|
18778
|
+
filters.runId = token.value;
|
|
18779
|
+
break;
|
|
18780
|
+
case "threadId":
|
|
18781
|
+
filters.threadId = token.value;
|
|
18782
|
+
break;
|
|
18783
|
+
case "sessionId":
|
|
18784
|
+
filters.sessionId = token.value;
|
|
18785
|
+
break;
|
|
18786
|
+
case "requestId":
|
|
18787
|
+
filters.requestId = token.value;
|
|
18788
|
+
break;
|
|
18789
|
+
case "resourceId":
|
|
18790
|
+
filters.resourceId = token.value;
|
|
18791
|
+
break;
|
|
18792
|
+
case "userId":
|
|
18793
|
+
filters.userId = token.value;
|
|
18794
|
+
break;
|
|
18795
|
+
case "organizationId":
|
|
18796
|
+
filters.organizationId = token.value;
|
|
18797
|
+
break;
|
|
18798
|
+
case "serviceName":
|
|
18799
|
+
filters.serviceName = token.value;
|
|
18800
|
+
break;
|
|
18801
|
+
case "environment":
|
|
18802
|
+
filters.environment = token.value;
|
|
18803
|
+
break;
|
|
18804
|
+
case "experimentId":
|
|
18805
|
+
filters.experimentId = token.value;
|
|
18806
|
+
break;
|
|
18807
|
+
}
|
|
18808
|
+
}
|
|
18809
|
+
return filters;
|
|
18810
|
+
}
|
|
18811
|
+
function neutralizeLogsFilterTokens(filterFields, filterTokens) {
|
|
18812
|
+
return filterTokens.map((token) => {
|
|
18813
|
+
const field = filterFields.find((f) => f.id === token.fieldId);
|
|
18814
|
+
if (!field) return token;
|
|
18815
|
+
if (field.kind === "text") return { fieldId: token.fieldId, value: "" };
|
|
18816
|
+
if (field.kind === "pick-multi") {
|
|
18817
|
+
return field.multi ? { fieldId: token.fieldId, value: [] } : { fieldId: token.fieldId, value: "Any" };
|
|
18818
|
+
}
|
|
18819
|
+
return token;
|
|
18820
|
+
});
|
|
18821
|
+
}
|
|
18822
|
+
|
|
18823
|
+
const LOG_PARAM = "logId";
|
|
18824
|
+
const TRACE_PARAM = "traceId";
|
|
18825
|
+
const SPAN_PARAM = "spanId";
|
|
18826
|
+
const DAY_MS = 24 * 60 * 60 * 1e3;
|
|
18827
|
+
const PRESET_MS = {
|
|
18828
|
+
"last-24h": DAY_MS,
|
|
18829
|
+
"last-3d": 3 * DAY_MS,
|
|
18830
|
+
"last-7d": 7 * DAY_MS,
|
|
18831
|
+
"last-14d": 14 * DAY_MS,
|
|
18832
|
+
"last-30d": 30 * DAY_MS
|
|
18833
|
+
};
|
|
18834
|
+
function clearSelectionParams(params) {
|
|
18835
|
+
params.delete(LOG_PARAM);
|
|
18836
|
+
params.delete(TRACE_PARAM);
|
|
18837
|
+
params.delete(SPAN_PARAM);
|
|
18838
|
+
}
|
|
18839
|
+
function useLogsUrlState(searchParams, setSearchParams, options) {
|
|
18840
|
+
const { onRemoveAll } = options ?? {};
|
|
18841
|
+
const datePreset = React.useMemo(() => {
|
|
18842
|
+
const value = searchParams.get(LOGS_DATE_PRESET_PARAM);
|
|
18843
|
+
return value && LOGS_DATE_PRESET_VALUES.has(value) ? value : "last-24h";
|
|
18844
|
+
}, [searchParams]);
|
|
18845
|
+
const dateFromParamRaw = searchParams.get(LOGS_DATE_FROM_PARAM);
|
|
18846
|
+
const dateToParamRaw = searchParams.get(LOGS_DATE_TO_PARAM);
|
|
18847
|
+
const selectedDateFrom = React.useMemo(() => {
|
|
18848
|
+
if (datePreset === "custom") {
|
|
18849
|
+
if (!dateFromParamRaw) return void 0;
|
|
18850
|
+
const parsed = new Date(dateFromParamRaw);
|
|
18851
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
18852
|
+
}
|
|
18853
|
+
if (datePreset === "all") return void 0;
|
|
18854
|
+
const ms = PRESET_MS[datePreset];
|
|
18855
|
+
return ms ? new Date(Date.now() - ms) : void 0;
|
|
18856
|
+
}, [datePreset, dateFromParamRaw]);
|
|
18857
|
+
const selectedDateTo = React.useMemo(() => {
|
|
18858
|
+
if (datePreset !== "custom" || !dateToParamRaw) return void 0;
|
|
18859
|
+
const parsed = new Date(dateToParamRaw);
|
|
18860
|
+
return Number.isNaN(parsed.getTime()) ? void 0 : parsed;
|
|
18861
|
+
}, [datePreset, dateToParamRaw]);
|
|
18862
|
+
const datePresetRef = React.useRef(datePreset);
|
|
18863
|
+
datePresetRef.current = datePreset;
|
|
18864
|
+
const featuredLogId = searchParams.get(LOG_PARAM);
|
|
18865
|
+
const featuredTraceId = searchParams.get(TRACE_PARAM);
|
|
18866
|
+
const featuredSpanId = searchParams.get(SPAN_PARAM);
|
|
18867
|
+
const selectedEntityOption = React.useMemo(
|
|
18868
|
+
() => LOGS_ROOT_ENTITY_TYPE_OPTIONS.find((option) => option.entityType === searchParams.get(LOGS_ROOT_ENTITY_TYPE_PARAM)),
|
|
18869
|
+
[searchParams]
|
|
18870
|
+
);
|
|
18871
|
+
const filterTokens = React.useMemo(() => getLogsPropertyFilterTokens(searchParams), [searchParams]);
|
|
18872
|
+
const handleFeaturedChange = React.useCallback(
|
|
18873
|
+
(ids) => {
|
|
18874
|
+
setSearchParams(
|
|
18875
|
+
(prev) => {
|
|
18876
|
+
const next = new URLSearchParams(prev);
|
|
18877
|
+
for (const [field, value] of Object.entries(ids)) {
|
|
18878
|
+
const param = field === "logId" ? LOG_PARAM : field === "traceId" ? TRACE_PARAM : SPAN_PARAM;
|
|
18879
|
+
if (value) {
|
|
18880
|
+
next.set(param, value);
|
|
18881
|
+
} else {
|
|
18882
|
+
next.delete(param);
|
|
18883
|
+
}
|
|
18884
|
+
}
|
|
18885
|
+
return next;
|
|
18886
|
+
},
|
|
18887
|
+
{ replace: true }
|
|
18888
|
+
);
|
|
18889
|
+
},
|
|
18890
|
+
[setSearchParams]
|
|
18891
|
+
);
|
|
18892
|
+
const applyFilterTokens = React.useCallback(
|
|
18893
|
+
(tokens) => {
|
|
18894
|
+
setSearchParams(
|
|
18895
|
+
(prev) => {
|
|
18896
|
+
const next = new URLSearchParams(prev);
|
|
18897
|
+
applyLogsPropertyFilterTokens(next, tokens);
|
|
18898
|
+
clearSelectionParams(next);
|
|
18899
|
+
return next;
|
|
18900
|
+
},
|
|
18901
|
+
{ replace: true }
|
|
18902
|
+
);
|
|
18903
|
+
},
|
|
18904
|
+
[setSearchParams]
|
|
18905
|
+
);
|
|
18906
|
+
const handleFilterTokensChange = applyFilterTokens;
|
|
18907
|
+
const handleDateChange = React.useCallback(
|
|
18908
|
+
(value, type) => {
|
|
18909
|
+
if (datePresetRef.current !== "custom") return;
|
|
18910
|
+
const param = type === "from" ? LOGS_DATE_FROM_PARAM : LOGS_DATE_TO_PARAM;
|
|
18911
|
+
setSearchParams(
|
|
18912
|
+
(prev) => {
|
|
18913
|
+
const next = new URLSearchParams(prev);
|
|
18914
|
+
if (value) {
|
|
18915
|
+
next.set(param, value.toISOString());
|
|
18916
|
+
} else {
|
|
18917
|
+
next.delete(param);
|
|
18918
|
+
}
|
|
18919
|
+
clearSelectionParams(next);
|
|
18920
|
+
return next;
|
|
18921
|
+
},
|
|
18922
|
+
{ replace: true }
|
|
18923
|
+
);
|
|
18924
|
+
},
|
|
18925
|
+
[setSearchParams]
|
|
18926
|
+
);
|
|
18927
|
+
const handleDatePresetChange = React.useCallback(
|
|
18928
|
+
(preset) => {
|
|
18929
|
+
datePresetRef.current = preset;
|
|
18930
|
+
setSearchParams(
|
|
18931
|
+
(prev) => {
|
|
18932
|
+
const next = new URLSearchParams(prev);
|
|
18933
|
+
if (preset === "last-24h") {
|
|
18934
|
+
next.delete(LOGS_DATE_PRESET_PARAM);
|
|
18935
|
+
next.delete(LOGS_DATE_FROM_PARAM);
|
|
18936
|
+
next.delete(LOGS_DATE_TO_PARAM);
|
|
18937
|
+
} else if (preset === "custom") {
|
|
18938
|
+
next.set(LOGS_DATE_PRESET_PARAM, "custom");
|
|
18939
|
+
} else {
|
|
18940
|
+
next.set(LOGS_DATE_PRESET_PARAM, preset);
|
|
18941
|
+
next.delete(LOGS_DATE_FROM_PARAM);
|
|
18942
|
+
next.delete(LOGS_DATE_TO_PARAM);
|
|
18943
|
+
}
|
|
18944
|
+
clearSelectionParams(next);
|
|
18945
|
+
return next;
|
|
18946
|
+
},
|
|
18947
|
+
{ replace: true }
|
|
18948
|
+
);
|
|
18949
|
+
},
|
|
18950
|
+
[setSearchParams]
|
|
18951
|
+
);
|
|
18952
|
+
const handleRemoveAll = React.useCallback(() => {
|
|
18953
|
+
setSearchParams(
|
|
18954
|
+
(prev) => {
|
|
18955
|
+
const next = new URLSearchParams(prev);
|
|
18956
|
+
next.delete(LOGS_ROOT_ENTITY_TYPE_PARAM);
|
|
18957
|
+
for (const fieldId of LOGS_PROPERTY_FILTER_FIELD_IDS) {
|
|
18958
|
+
next.delete(LOGS_PROPERTY_FILTER_PARAM_BY_FIELD[fieldId]);
|
|
18959
|
+
}
|
|
18960
|
+
next.delete(LOGS_DATE_PRESET_PARAM);
|
|
18961
|
+
next.delete(LOGS_DATE_FROM_PARAM);
|
|
18962
|
+
next.delete(LOGS_DATE_TO_PARAM);
|
|
18963
|
+
clearSelectionParams(next);
|
|
18964
|
+
return next;
|
|
18965
|
+
},
|
|
18966
|
+
{ replace: true }
|
|
18967
|
+
);
|
|
18968
|
+
onRemoveAll?.();
|
|
18969
|
+
}, [setSearchParams, onRemoveAll]);
|
|
18970
|
+
return React.useMemo(
|
|
18971
|
+
() => ({
|
|
18972
|
+
searchParams,
|
|
18973
|
+
setSearchParams,
|
|
18974
|
+
datePreset,
|
|
18975
|
+
selectedDateFrom,
|
|
18976
|
+
selectedDateTo,
|
|
18977
|
+
datePresetRef,
|
|
18978
|
+
featuredLogId,
|
|
18979
|
+
featuredTraceId,
|
|
18980
|
+
featuredSpanId,
|
|
18981
|
+
selectedEntityOption,
|
|
18982
|
+
filterTokens,
|
|
18983
|
+
handleFeaturedChange,
|
|
18984
|
+
handleFilterTokensChange,
|
|
18985
|
+
handleDateChange,
|
|
18986
|
+
handleDatePresetChange,
|
|
18987
|
+
handleRemoveAll,
|
|
18988
|
+
applyFilterTokens
|
|
18989
|
+
}),
|
|
18990
|
+
[
|
|
18991
|
+
searchParams,
|
|
18992
|
+
setSearchParams,
|
|
18993
|
+
datePreset,
|
|
18994
|
+
selectedDateFrom,
|
|
18995
|
+
selectedDateTo,
|
|
18996
|
+
datePresetRef,
|
|
18997
|
+
featuredLogId,
|
|
18998
|
+
featuredTraceId,
|
|
18999
|
+
featuredSpanId,
|
|
19000
|
+
selectedEntityOption,
|
|
19001
|
+
filterTokens,
|
|
19002
|
+
handleFeaturedChange,
|
|
19003
|
+
handleFilterTokensChange,
|
|
19004
|
+
handleDateChange,
|
|
19005
|
+
handleDatePresetChange,
|
|
19006
|
+
handleRemoveAll,
|
|
19007
|
+
applyFilterTokens
|
|
19008
|
+
]
|
|
19009
|
+
);
|
|
19010
|
+
}
|
|
19011
|
+
|
|
19012
|
+
const DEFAULT_SAVED_MESSAGE = "Filters setting for Logs saved";
|
|
19013
|
+
const DEFAULT_CLEARED_MESSAGE = "Filters setting for Logs cleared up";
|
|
19014
|
+
function useLogsFilterPersistence(searchParams, setSearchParams, options) {
|
|
19015
|
+
const { storageKey, messages, skipHydration } = options ?? {};
|
|
19016
|
+
const [hasSavedFilters, setHasSavedFilters] = React.useState(() => loadLogsFiltersFromStorage(storageKey) !== null);
|
|
19017
|
+
const handleSave = React.useCallback(() => {
|
|
19018
|
+
saveLogsFiltersToStorage(searchParams, storageKey);
|
|
19019
|
+
setHasSavedFilters(true);
|
|
19020
|
+
const text = messages?.saved ?? DEFAULT_SAVED_MESSAGE;
|
|
19021
|
+
if (text !== false) toast.success(text);
|
|
19022
|
+
}, [searchParams, storageKey, messages?.saved]);
|
|
19023
|
+
const handleRemoveSaved = React.useCallback(() => {
|
|
19024
|
+
clearSavedLogsFilters(storageKey);
|
|
19025
|
+
setHasSavedFilters(false);
|
|
19026
|
+
const text = messages?.cleared ?? DEFAULT_CLEARED_MESSAGE;
|
|
19027
|
+
if (text !== false) toast.success(text);
|
|
19028
|
+
}, [storageKey, messages?.cleared]);
|
|
19029
|
+
const hydratedRef = React.useRef(false);
|
|
19030
|
+
React.useEffect(() => {
|
|
19031
|
+
if (skipHydration) return;
|
|
19032
|
+
if (hydratedRef.current) return;
|
|
19033
|
+
hydratedRef.current = true;
|
|
19034
|
+
if (hasAnyLogsFilterParams(searchParams)) return;
|
|
19035
|
+
const saved = loadLogsFiltersFromStorage(storageKey);
|
|
19036
|
+
if (!saved) return;
|
|
19037
|
+
setSearchParams(
|
|
19038
|
+
(prev) => {
|
|
19039
|
+
const next = new URLSearchParams(prev);
|
|
19040
|
+
for (const [key, value] of saved) {
|
|
19041
|
+
next.append(key, value);
|
|
19042
|
+
}
|
|
19043
|
+
return next;
|
|
19044
|
+
},
|
|
19045
|
+
{ replace: true }
|
|
19046
|
+
);
|
|
19047
|
+
}, []);
|
|
19048
|
+
return { hasSavedFilters, handleSave, handleRemoveSaved };
|
|
19049
|
+
}
|
|
19050
|
+
|
|
19051
|
+
function hashCode(str) {
|
|
19052
|
+
let hash = 0;
|
|
19053
|
+
for (let i = 0; i < str.length; i++) {
|
|
19054
|
+
hash = (hash << 5) - hash + str.charCodeAt(i) | 0;
|
|
19055
|
+
}
|
|
19056
|
+
return (hash >>> 0).toString(36);
|
|
19057
|
+
}
|
|
19058
|
+
function buildLogIds(logs) {
|
|
19059
|
+
const ids = /* @__PURE__ */ new Map();
|
|
19060
|
+
for (const log of logs) {
|
|
19061
|
+
if (log.logId) {
|
|
19062
|
+
ids.set(log, log.logId);
|
|
19063
|
+
continue;
|
|
19064
|
+
}
|
|
19065
|
+
const ts = log.timestamp instanceof Date ? log.timestamp.toISOString() : log.timestamp;
|
|
19066
|
+
ids.set(log, hashCode(`${ts}${log.message ?? ""}${log.data ? JSON.stringify(log.data) : ""}`));
|
|
19067
|
+
}
|
|
19068
|
+
return ids;
|
|
19069
|
+
}
|
|
19070
|
+
function useLogsListNavigation(logs, featuredLogId, onFeaturedChange, featuredTraceId) {
|
|
19071
|
+
const logIdMap = React.useMemo(() => buildLogIds(logs), [logs]);
|
|
19072
|
+
const idToLog = React.useMemo(() => {
|
|
19073
|
+
const m = /* @__PURE__ */ new Map();
|
|
19074
|
+
for (let i = 0; i < logs.length; i++) {
|
|
19075
|
+
const id = logIdMap.get(logs[i]);
|
|
19076
|
+
if (id) m.set(id, { log: logs[i], idx: i });
|
|
19077
|
+
}
|
|
19078
|
+
return m;
|
|
19079
|
+
}, [logs, logIdMap]);
|
|
19080
|
+
const entry = featuredLogId ? idToLog.get(featuredLogId) : void 0;
|
|
19081
|
+
const featuredLogIdx = entry?.idx ?? -1;
|
|
19082
|
+
const featuredLog = featuredLogIdx >= 0 ? logs[featuredLogIdx] : null;
|
|
19083
|
+
const getLogId = React.useCallback((log) => logIdMap.get(log), [logIdMap]);
|
|
19084
|
+
const handleLogClick = React.useCallback(
|
|
19085
|
+
(log) => {
|
|
19086
|
+
const id = logIdMap.get(log);
|
|
19087
|
+
if (!id) return;
|
|
19088
|
+
if (featuredLogId === id) {
|
|
19089
|
+
onFeaturedChange({ logId: null });
|
|
19090
|
+
return;
|
|
19091
|
+
}
|
|
19092
|
+
if (featuredTraceId) {
|
|
19093
|
+
onFeaturedChange({ logId: id, traceId: log.traceId ?? null, spanId: null });
|
|
19094
|
+
} else {
|
|
19095
|
+
onFeaturedChange({ logId: id });
|
|
19096
|
+
}
|
|
19097
|
+
},
|
|
19098
|
+
[logIdMap, featuredLogId, featuredTraceId, onFeaturedChange]
|
|
19099
|
+
);
|
|
19100
|
+
const handlePreviousLog = featuredLogIdx > 0 ? () => {
|
|
19101
|
+
const prevLog = logs[featuredLogIdx - 1];
|
|
19102
|
+
const id = logIdMap.get(prevLog);
|
|
19103
|
+
if (featuredTraceId) {
|
|
19104
|
+
onFeaturedChange({ logId: id, traceId: prevLog.traceId ?? null, spanId: null });
|
|
19105
|
+
} else {
|
|
19106
|
+
onFeaturedChange({ logId: id });
|
|
19107
|
+
}
|
|
19108
|
+
} : void 0;
|
|
19109
|
+
const handleNextLog = featuredLogIdx >= 0 && featuredLogIdx < logs.length - 1 ? () => {
|
|
19110
|
+
const nextLog = logs[featuredLogIdx + 1];
|
|
19111
|
+
const id = logIdMap.get(nextLog);
|
|
19112
|
+
if (featuredTraceId) {
|
|
19113
|
+
onFeaturedChange({ logId: id, traceId: nextLog.traceId ?? null, spanId: null });
|
|
19114
|
+
} else {
|
|
19115
|
+
onFeaturedChange({ logId: id });
|
|
19116
|
+
}
|
|
19117
|
+
} : void 0;
|
|
19118
|
+
return {
|
|
19119
|
+
logIdMap,
|
|
19120
|
+
getLogId,
|
|
19121
|
+
featuredLog,
|
|
19122
|
+
handleLogClick,
|
|
19123
|
+
handlePreviousLog,
|
|
19124
|
+
handleNextLog
|
|
19125
|
+
};
|
|
19126
|
+
}
|
|
19127
|
+
|
|
15007
19128
|
exports.Animations = tokens.Animations;
|
|
15008
19129
|
exports.BorderColors = tokens.BorderColors;
|
|
15009
19130
|
exports.BorderRadius = tokens.BorderRadius;
|
|
@@ -15034,12 +19155,15 @@ exports.ApiIcon = ApiIcon;
|
|
|
15034
19155
|
exports.Avatar = Avatar;
|
|
15035
19156
|
exports.AzureIcon = AzureIcon;
|
|
15036
19157
|
exports.Badge = Badge;
|
|
19158
|
+
exports.BarListContent = BarListContent;
|
|
15037
19159
|
exports.BranchIcon = BranchIcon;
|
|
15038
19160
|
exports.BrandLoader = BrandLoader;
|
|
15039
19161
|
exports.Breadcrumb = Breadcrumb;
|
|
15040
19162
|
exports.Button = Button;
|
|
15041
19163
|
exports.ButtonWithTooltip = ButtonWithTooltip;
|
|
15042
19164
|
exports.ButtonsGroup = ButtonsGroup;
|
|
19165
|
+
exports.CHART_COLORS = CHART_COLORS;
|
|
19166
|
+
exports.CONTEXT_FIELD_IDS = CONTEXT_FIELD_IDS;
|
|
15043
19167
|
exports.Card = Card;
|
|
15044
19168
|
exports.CardContent = CardContent;
|
|
15045
19169
|
exports.CardDescription = CardDescription;
|
|
@@ -15078,6 +19202,9 @@ exports.ContentBlocks = ContentBlocks;
|
|
|
15078
19202
|
exports.CopyButton = CopyButton;
|
|
15079
19203
|
exports.CrossIcon = CrossIcon;
|
|
15080
19204
|
exports.Crumb = Crumb;
|
|
19205
|
+
exports.DATE_PRESETS = DATE_PRESETS;
|
|
19206
|
+
exports.DEFAULT_LOGS_FILTERS_STORAGE_KEY = DEFAULT_LOGS_FILTERS_STORAGE_KEY;
|
|
19207
|
+
exports.DEFAULT_TRACE_FILTERS_STORAGE_KEY = DEFAULT_TRACE_FILTERS_STORAGE_KEY;
|
|
15081
19208
|
exports.DashboardCard = DashboardCard;
|
|
15082
19209
|
exports.DataCodeSection = DataCodeSection;
|
|
15083
19210
|
exports.DataDetailsPanel = DataDetailsPanel;
|
|
@@ -15086,6 +19213,7 @@ exports.DataList = DataList;
|
|
|
15086
19213
|
exports.DataListSkeleton = DataListSkeleton;
|
|
15087
19214
|
exports.DataPanel = DataPanel;
|
|
15088
19215
|
exports.DatePicker = DatePicker;
|
|
19216
|
+
exports.DateRangeSelector = DateRangeSelector;
|
|
15089
19217
|
exports.DateTimeCell = DateTimeCell;
|
|
15090
19218
|
exports.DateTimePicker = DateTimePicker;
|
|
15091
19219
|
exports.DateTimePickerContent = DateTimePickerContent;
|
|
@@ -15160,14 +19288,32 @@ exports.JSONSchemaForm = JSONSchemaForm;
|
|
|
15160
19288
|
exports.JudgeIcon = JudgeIcon;
|
|
15161
19289
|
exports.Kbd = Kbd;
|
|
15162
19290
|
exports.KeyValueList = KeyValueList;
|
|
19291
|
+
exports.KpiCardView = KpiCardView;
|
|
19292
|
+
exports.LOGS_DATE_FROM_PARAM = LOGS_DATE_FROM_PARAM;
|
|
19293
|
+
exports.LOGS_DATE_PRESET_PARAM = LOGS_DATE_PRESET_PARAM;
|
|
19294
|
+
exports.LOGS_DATE_PRESET_VALUES = LOGS_DATE_PRESET_VALUES;
|
|
19295
|
+
exports.LOGS_DATE_TO_PARAM = LOGS_DATE_TO_PARAM;
|
|
19296
|
+
exports.LOGS_PROPERTY_FILTER_FIELD_IDS = LOGS_PROPERTY_FILTER_FIELD_IDS;
|
|
19297
|
+
exports.LOGS_PROPERTY_FILTER_PARAM_BY_FIELD = LOGS_PROPERTY_FILTER_PARAM_BY_FIELD;
|
|
19298
|
+
exports.LOGS_ROOT_ENTITY_TYPES = LOGS_ROOT_ENTITY_TYPES;
|
|
19299
|
+
exports.LOGS_ROOT_ENTITY_TYPE_OPTIONS = LOGS_ROOT_ENTITY_TYPE_OPTIONS;
|
|
19300
|
+
exports.LOGS_ROOT_ENTITY_TYPE_PARAM = LOGS_ROOT_ENTITY_TYPE_PARAM;
|
|
19301
|
+
exports.LOG_LEVEL_OPTIONS = LOG_LEVEL_OPTIONS;
|
|
19302
|
+
exports.LOG_LEVEL_VALUES = LOG_LEVEL_VALUES;
|
|
15163
19303
|
exports.Label = Label;
|
|
19304
|
+
exports.LatencyCardView = LatencyCardView;
|
|
15164
19305
|
exports.LatencyIcon = LatencyIcon;
|
|
15165
19306
|
exports.ListSearch = ListSearch;
|
|
19307
|
+
exports.LogDetailsView = LogDetailsView;
|
|
15166
19308
|
exports.Logo = Logo;
|
|
15167
19309
|
exports.LogoWithoutText = LogoWithoutText;
|
|
15168
19310
|
exports.LogsDataList = LogsDataList;
|
|
15169
19311
|
exports.LogsDataListSkeleton = DataListSkeleton;
|
|
19312
|
+
exports.LogsErrorContent = LogsErrorContent;
|
|
15170
19313
|
exports.LogsIcon = LogsIcon;
|
|
19314
|
+
exports.LogsLayout = LogsLayout;
|
|
19315
|
+
exports.LogsListView = LogsListView;
|
|
19316
|
+
exports.LogsToolbar = LogsToolbar;
|
|
15171
19317
|
exports.MainContentContent = MainContentContent;
|
|
15172
19318
|
exports.MainContentLayout = MainContentLayout;
|
|
15173
19319
|
exports.MainHeader = MainHeader;
|
|
@@ -15185,12 +19331,15 @@ exports.MetricsFlexGrid = MetricsFlexGrid;
|
|
|
15185
19331
|
exports.MetricsKpiCard = MetricsKpiCard;
|
|
15186
19332
|
exports.MetricsLineChart = MetricsLineChart;
|
|
15187
19333
|
exports.MetricsLineChartTooltip = MetricsLineChartTooltip;
|
|
19334
|
+
exports.MetricsProvider = MetricsProvider;
|
|
15188
19335
|
exports.MistralIcon = MistralIcon;
|
|
19336
|
+
exports.ModelUsageCostCardView = ModelUsageCostCardView;
|
|
15189
19337
|
exports.MultiColumn = MultiColumn;
|
|
15190
19338
|
exports.MultiCombobox = MultiCombobox;
|
|
15191
19339
|
exports.NestedFields = NestedFields;
|
|
15192
19340
|
exports.NetlifyIcon = NetlifyIcon;
|
|
15193
19341
|
exports.NoDataPageLayout = NoDataPageLayout;
|
|
19342
|
+
exports.NoLogsInfo = NoLogsInfo;
|
|
15194
19343
|
exports.Notice = Notice;
|
|
15195
19344
|
exports.Notification = Notification;
|
|
15196
19345
|
exports.OPERATORS = OPERATORS;
|
|
@@ -15214,6 +19363,8 @@ exports.PromptIcon = PromptIcon;
|
|
|
15214
19363
|
exports.PropertyFilterActions = PropertyFilterActions;
|
|
15215
19364
|
exports.PropertyFilterApplied = PropertyFilterApplied;
|
|
15216
19365
|
exports.PropertyFilterCreator = PropertyFilterCreator;
|
|
19366
|
+
exports.ROOT_ENTITY_TYPES = ROOT_ENTITY_TYPES;
|
|
19367
|
+
exports.ROOT_ENTITY_TYPE_OPTIONS = ROOT_ENTITY_TYPE_OPTIONS;
|
|
15217
19368
|
exports.RadioGroup = RadioGroup;
|
|
15218
19369
|
exports.RadioGroupItem = RadioGroupItem;
|
|
15219
19370
|
exports.RepoIcon = RepoIcon;
|
|
@@ -15224,6 +19375,7 @@ exports.RuleFieldSelect = RuleFieldSelect;
|
|
|
15224
19375
|
exports.RuleOperatorSelect = RuleOperatorSelect;
|
|
15225
19376
|
exports.RuleRow = RuleRow;
|
|
15226
19377
|
exports.RuleValueInput = RuleValueInput;
|
|
19378
|
+
exports.ScoresCardView = ScoresCardView;
|
|
15227
19379
|
exports.ScoresDataList = ScoresDataList;
|
|
15228
19380
|
exports.ScrollArea = ScrollArea;
|
|
15229
19381
|
exports.ScrollBar = ScrollBar;
|
|
@@ -15250,10 +19402,25 @@ exports.Skeleton = Skeleton;
|
|
|
15250
19402
|
exports.SkillIcon = SkillIcon;
|
|
15251
19403
|
exports.SlashIcon = SlashIcon;
|
|
15252
19404
|
exports.Slider = Slider;
|
|
19405
|
+
exports.SpanDataPanelView = SpanDataPanelView;
|
|
19406
|
+
exports.SpanDetailsView = SpanDetailsView;
|
|
19407
|
+
exports.SpanTokenUsage = SpanTokenUsage;
|
|
15253
19408
|
exports.Spinner = Spinner;
|
|
19409
|
+
exports.StackedRunsBars = StackedRunsBars;
|
|
15254
19410
|
exports.StatusBadge = StatusBadge;
|
|
15255
19411
|
exports.SubSectionRoot = SubSectionRoot;
|
|
15256
19412
|
exports.Switch = Switch;
|
|
19413
|
+
exports.TRACE_DATE_FROM_PARAM = TRACE_DATE_FROM_PARAM;
|
|
19414
|
+
exports.TRACE_DATE_PRESET_PARAM = TRACE_DATE_PRESET_PARAM;
|
|
19415
|
+
exports.TRACE_DATE_PRESET_VALUES = TRACE_DATE_PRESET_VALUES;
|
|
19416
|
+
exports.TRACE_DATE_TO_PARAM = TRACE_DATE_TO_PARAM;
|
|
19417
|
+
exports.TRACE_PROPERTY_FILTER_FIELD_IDS = TRACE_PROPERTY_FILTER_FIELD_IDS;
|
|
19418
|
+
exports.TRACE_PROPERTY_FILTER_PARAM_BY_FIELD = TRACE_PROPERTY_FILTER_PARAM_BY_FIELD;
|
|
19419
|
+
exports.TRACE_ROOT_ENTITY_TYPE_PARAM = TRACE_ROOT_ENTITY_TYPE_PARAM;
|
|
19420
|
+
exports.TRACE_STATUS_OPTIONS = TRACE_STATUS_OPTIONS;
|
|
19421
|
+
exports.TRACE_STATUS_PARAM = TRACE_STATUS_PARAM;
|
|
19422
|
+
exports.TRACE_STATUS_VALUES = TRACE_STATUS_VALUES;
|
|
19423
|
+
exports.TRACE_SYNTHETIC_FILTER_FIELD_IDS = TRACE_SYNTHETIC_FILTER_FIELD_IDS;
|
|
15257
19424
|
exports.Tab = Tab;
|
|
15258
19425
|
exports.TabContent = TabContent;
|
|
15259
19426
|
exports.TabList = TabList;
|
|
@@ -15271,14 +19438,29 @@ exports.ThreadLink = ThreadLink;
|
|
|
15271
19438
|
exports.ThreadList = ThreadList;
|
|
15272
19439
|
exports.Threads = Threads;
|
|
15273
19440
|
exports.TimePicker = TimePicker;
|
|
19441
|
+
exports.TimelineExpandCol = TimelineExpandCol;
|
|
19442
|
+
exports.TimelineNameCol = TimelineNameCol;
|
|
19443
|
+
exports.TimelineStructureSign = TimelineStructureSign;
|
|
19444
|
+
exports.TimelineTimingCol = TimelineTimingCol;
|
|
19445
|
+
exports.TokenUsageByAgentCardView = TokenUsageByAgentCardView;
|
|
15274
19446
|
exports.ToolCoinIcon = ToolCoinIcon;
|
|
15275
19447
|
exports.ToolsIcon = ToolsIcon;
|
|
15276
19448
|
exports.Tooltip = Tooltip;
|
|
15277
19449
|
exports.TooltipContent = TooltipContent;
|
|
15278
19450
|
exports.TooltipProvider = TooltipProvider;
|
|
15279
19451
|
exports.TooltipTrigger = TooltipTrigger;
|
|
19452
|
+
exports.TraceDataPanelView = TraceDataPanelView;
|
|
19453
|
+
exports.TraceDetailsView = TraceDetailsView;
|
|
15280
19454
|
exports.TraceIcon = TraceIcon;
|
|
19455
|
+
exports.TraceKeysAndValues = TraceKeysAndValues;
|
|
19456
|
+
exports.TraceTimeline = TraceTimeline;
|
|
19457
|
+
exports.TraceTimelineSpan = TraceTimelineSpan;
|
|
15281
19458
|
exports.TracesDataList = TracesDataList;
|
|
19459
|
+
exports.TracesErrorContent = TracesErrorContent;
|
|
19460
|
+
exports.TracesLayout = TracesLayout;
|
|
19461
|
+
exports.TracesListView = TracesListView;
|
|
19462
|
+
exports.TracesToolbar = TracesToolbar;
|
|
19463
|
+
exports.TracesVolumeCardView = TracesVolumeCardView;
|
|
15282
19464
|
exports.Tree = Tree;
|
|
15283
19465
|
exports.Truncate = Truncate;
|
|
15284
19466
|
exports.TsIcon = TsIcon;
|
|
@@ -15289,13 +19471,21 @@ exports.VariablesIcon = VariablesIcon;
|
|
|
15289
19471
|
exports.WorkflowCoinIcon = WorkflowCoinIcon;
|
|
15290
19472
|
exports.WorkflowIcon = WorkflowIcon;
|
|
15291
19473
|
exports.XGroqIcon = XGroqIcon;
|
|
19474
|
+
exports.applyLogsPropertyFilterTokens = applyLogsPropertyFilterTokens;
|
|
19475
|
+
exports.applyTracePropertyFilterTokens = applyTracePropertyFilterTokens;
|
|
19476
|
+
exports.buildLogsListFilters = buildLogsListFilters;
|
|
19477
|
+
exports.buildTraceListFilters = buildTraceListFilters;
|
|
15292
19478
|
exports.buttonVariants = buttonVariants;
|
|
19479
|
+
exports.clearSavedLogsFilters = clearSavedLogsFilters;
|
|
19480
|
+
exports.clearSavedTraceFilters = clearSavedTraceFilters;
|
|
15293
19481
|
exports.cn = cn;
|
|
15294
19482
|
exports.comboboxStyles = comboboxStyles;
|
|
15295
19483
|
exports.countLeafRules = countLeafRules;
|
|
15296
19484
|
exports.createDefaultRule = createDefaultRule;
|
|
15297
19485
|
exports.createDefaultRuleGroup = createDefaultRuleGroup;
|
|
15298
19486
|
exports.createField = createField;
|
|
19487
|
+
exports.createLogsPropertyFilterFields = createLogsPropertyFilterFields;
|
|
19488
|
+
exports.createTracePropertyFilterFields = createTracePropertyFilterFields;
|
|
15299
19489
|
exports.createVariableAutocomplete = createVariableAutocomplete;
|
|
15300
19490
|
exports.fieldsToJSONSchema = fieldsToJSONSchema;
|
|
15301
19491
|
exports.fileToBase64 = fileToBase64;
|
|
@@ -15306,21 +19496,36 @@ exports.formElementFocusWithin = formElementFocusWithin;
|
|
|
15306
19496
|
exports.formElementRadius = formElementRadius;
|
|
15307
19497
|
exports.formElementSizes = formElementSizes;
|
|
15308
19498
|
exports.formElementTransition = formElementTransition;
|
|
19499
|
+
exports.formatCompact = formatCompact;
|
|
19500
|
+
exports.formatCost = formatCost;
|
|
19501
|
+
exports.formatHierarchicalSpans = formatHierarchicalSpans;
|
|
15309
19502
|
exports.formatJSON = formatJSON;
|
|
15310
19503
|
exports.generateDefaultValues = generateDefaultValues;
|
|
19504
|
+
exports.getAllSpanIds = getAllSpanIds;
|
|
15311
19505
|
exports.getChildFieldOptions = getChildFieldOptions;
|
|
15312
19506
|
exports.getColumnTemplate = getColumnTemplate;
|
|
15313
19507
|
exports.getFieldOptionAtPath = getFieldOptionAtPath;
|
|
15314
19508
|
exports.getFieldOptionsFromSchema = getFieldOptionsFromSchema;
|
|
15315
19509
|
exports.getFileContentType = getFileContentType;
|
|
19510
|
+
exports.getInputPreview = getInputPreview;
|
|
15316
19511
|
exports.getItemListColumnTemplate = getItemListColumnTemplate;
|
|
19512
|
+
exports.getLogsPropertyFilterTokens = getLogsPropertyFilterTokens;
|
|
15317
19513
|
exports.getMainContentContentClassName = getMainContentContentClassName;
|
|
19514
|
+
exports.getPreservedLogsFilterParams = getPreservedLogsFilterParams;
|
|
19515
|
+
exports.getPreservedTraceFilterParams = getPreservedTraceFilterParams;
|
|
15318
19516
|
exports.getShortId = getShortId$1;
|
|
19517
|
+
exports.getSpanDescendantIds = getSpanDescendantIds;
|
|
19518
|
+
exports.getSpanTypeUi = getSpanTypeUi;
|
|
15319
19519
|
exports.getStatusIcon = getStatusIcon;
|
|
15320
19520
|
exports.getToNextEntryFn = getToNextEntryFn;
|
|
15321
19521
|
exports.getToNextItemFn = getToNextItemFn;
|
|
15322
19522
|
exports.getToPreviousEntryFn = getToPreviousEntryFn;
|
|
15323
19523
|
exports.getToPreviousItemFn = getToPreviousItemFn;
|
|
19524
|
+
exports.getTokenLimitMessage = getTokenLimitMessage;
|
|
19525
|
+
exports.getTracePropertyFilterTokens = getTracePropertyFilterTokens;
|
|
19526
|
+
exports.groupTracesByThread = groupTracesByThread;
|
|
19527
|
+
exports.hasAnyLogsFilterParams = hasAnyLogsFilterParams;
|
|
19528
|
+
exports.hasAnyTraceFilterParams = hasAnyTraceFilterParams;
|
|
15324
19529
|
exports.highlight = highlight;
|
|
15325
19530
|
exports.hoverEffects = hoverEffects;
|
|
15326
19531
|
exports.inputVariants = inputVariants;
|
|
@@ -15330,32 +19535,68 @@ exports.isNonRetryableError = isNonRetryableError;
|
|
|
15330
19535
|
exports.isObjectEmpty = isObjectEmpty;
|
|
15331
19536
|
exports.isRule = isRule;
|
|
15332
19537
|
exports.isRuleGroup = isRuleGroup;
|
|
19538
|
+
exports.isTokenLimitExceeded = isTokenLimitExceeded;
|
|
15333
19539
|
exports.isValidJson = isValidJson;
|
|
19540
|
+
exports.isValidPreset = isValidPreset;
|
|
15334
19541
|
exports.jsonSchemaToFields = jsonSchemaToFields;
|
|
19542
|
+
exports.loadLogsFiltersFromStorage = loadLogsFiltersFromStorage;
|
|
19543
|
+
exports.loadTraceFiltersFromStorage = loadTraceFiltersFromStorage;
|
|
15335
19544
|
exports.lodashTitleCase = lodashTitleCase;
|
|
19545
|
+
exports.neutralizeFilterTokens = neutralizeFilterTokens;
|
|
19546
|
+
exports.neutralizeLogsFilterTokens = neutralizeLogsFilterTokens;
|
|
15336
19547
|
exports.parseError = parseError;
|
|
15337
19548
|
exports.parseFieldPath = parseFieldPath;
|
|
19549
|
+
exports.saveLogsFiltersToStorage = saveLogsFiltersToStorage;
|
|
19550
|
+
exports.saveTraceFiltersToStorage = saveTraceFiltersToStorage;
|
|
15338
19551
|
exports.sharedFormElementDisabledStyle = sharedFormElementDisabledStyle;
|
|
15339
19552
|
exports.sharedFormElementFocusStyle = sharedFormElementFocusStyle;
|
|
15340
19553
|
exports.sharedFormElementStyle = sharedFormElementStyle;
|
|
15341
19554
|
exports.shouldRetryQuery = shouldRetryQuery;
|
|
19555
|
+
exports.spanTypePrefixes = spanTypePrefixes;
|
|
15342
19556
|
exports.stringToColor = stringToColor;
|
|
15343
19557
|
exports.textareaVariants = textareaVariants;
|
|
15344
19558
|
exports.toSigFigs = toSigFigs;
|
|
15345
19559
|
exports.toast = toast;
|
|
15346
19560
|
exports.transitions = transitions;
|
|
15347
19561
|
exports.truncateString = truncateString;
|
|
19562
|
+
exports.useAgentRunsKpiMetrics = useAgentRunsKpiMetrics;
|
|
15348
19563
|
exports.useAutoscroll = useAutoscroll;
|
|
19564
|
+
exports.useAvgScoreKpiMetrics = useAvgScoreKpiMetrics;
|
|
15349
19565
|
exports.useCodemirrorTheme = useCodemirrorTheme$3;
|
|
15350
19566
|
exports.useCopyToClipboard = useCopyToClipboard;
|
|
19567
|
+
exports.useEntityNames = useEntityNames;
|
|
19568
|
+
exports.useEnvironments = useEnvironments;
|
|
15351
19569
|
exports.useInView = useInView;
|
|
15352
19570
|
exports.useIsDarkMode = useIsDarkMode;
|
|
15353
19571
|
exports.useJSONSchemaForm = useJSONSchemaForm;
|
|
15354
19572
|
exports.useJSONSchemaFormField = useJSONSchemaFormField;
|
|
15355
19573
|
exports.useJSONSchemaFormNestedContext = useJSONSchemaFormNestedContext;
|
|
19574
|
+
exports.useLatencyMetrics = useLatencyMetrics;
|
|
19575
|
+
exports.useLogs = useLogs;
|
|
19576
|
+
exports.useLogsFilterPersistence = useLogsFilterPersistence;
|
|
19577
|
+
exports.useLogsListNavigation = useLogsListNavigation;
|
|
19578
|
+
exports.useLogsUrlState = useLogsUrlState;
|
|
15356
19579
|
exports.useMainSidebar = useMainSidebar;
|
|
15357
19580
|
exports.useMaybeSidebar = useMaybeSidebar;
|
|
19581
|
+
exports.useMetrics = useMetrics;
|
|
19582
|
+
exports.useMetricsFilters = useMetricsFilters;
|
|
19583
|
+
exports.useModelCostKpiMetrics = useModelCostKpiMetrics;
|
|
19584
|
+
exports.useModelUsageCostMetrics = useModelUsageCostMetrics;
|
|
15358
19585
|
exports.usePlaygroundStore = usePlaygroundStore;
|
|
19586
|
+
exports.useScoresMetrics = useScoresMetrics;
|
|
19587
|
+
exports.useServiceNames = useServiceNames;
|
|
19588
|
+
exports.useSpanDetail = useSpanDetail;
|
|
15359
19589
|
exports.useTableKeyboardNavigation = useTableKeyboardNavigation;
|
|
19590
|
+
exports.useTags = useTags;
|
|
19591
|
+
exports.useTokenUsageByAgentMetrics = useTokenUsageByAgentMetrics;
|
|
19592
|
+
exports.useTotalTokensKpiMetrics = useTotalTokensKpiMetrics;
|
|
19593
|
+
exports.useTraceFilterPersistence = useTraceFilterPersistence;
|
|
19594
|
+
exports.useTraceLightSpans = useTraceLightSpans;
|
|
19595
|
+
exports.useTraceListNavigation = useTraceListNavigation;
|
|
19596
|
+
exports.useTraceSpanNavigation = useTraceSpanNavigation;
|
|
19597
|
+
exports.useTraceSpans = useTraceSpans;
|
|
19598
|
+
exports.useTraceUrlState = useTraceUrlState;
|
|
19599
|
+
exports.useTraceVolumeMetrics = useTraceVolumeMetrics;
|
|
19600
|
+
exports.useTraces = useTraces;
|
|
15360
19601
|
exports.variableHighlight = variableHighlight;
|
|
15361
19602
|
//# sourceMappingURL=index.cjs.js.map
|