@burdenoff/fluidgrids-fe-sdk 2026.1105.1 → 2026.1105.2
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/dist/{ExecutionDetailPage-YBAMVPMB.js → ExecutionDetailPage-HQB2SWBP.js} +12 -10
- package/dist/{ExecutionsListPage-NQIQXMTY.js → ExecutionsListPage-RSLL634L.js} +8 -7
- package/dist/{WorkflowBuilder-CRR5WP4V.js → WorkflowBuilder-E3PNZX74.js} +12 -12
- package/dist/{WorkflowsList-EV725VG5.js → WorkflowsList-473FRRP2.js} +7 -7
- package/dist/{agentTools-PN4W7QZH.js → agentTools-5N3ZEZDW.js} +4 -4
- package/dist/{chunk-AZKVI4YN.js → chunk-2KUZBEWW.js} +5 -4
- package/dist/{chunk-C26QAWCT.js → chunk-3QCRKKTI.js} +1 -1
- package/dist/{chunk-L44BRLW5.js → chunk-3XUWPDKJ.js} +612 -22
- package/dist/{chunk-DANOMJ5I.js → chunk-5CFCFA6O.js} +6 -6
- package/dist/{chunk-I3QPAEL6.js → chunk-62CCBMDC.js} +12 -12
- package/dist/{chunk-4QB6S7YW.js → chunk-64JN2TVX.js} +2 -2
- package/dist/{chunk-D7G5VWSH.js → chunk-6IS7EXIY.js} +2 -2
- package/dist/{chunk-FJSXXO5W.js → chunk-6ZK4MRVA.js} +1 -1
- package/dist/{chunk-2QOAJF7V.js → chunk-AJVU5YW6.js} +2 -2
- package/dist/{chunk-JDQYGH4Q.js → chunk-CH3YUGWR.js} +4 -4
- package/dist/{chunk-MSQAULLG.js → chunk-CNKTQ5MG.js} +186 -7
- package/dist/{chunk-6P7DOBVN.js → chunk-EYQ7GJRW.js} +2 -2
- package/dist/{chunk-O2EB7UT2.js → chunk-GKFMK7FF.js} +2 -2
- package/dist/{chunk-6EZJ5J3B.js → chunk-HK76ZUAK.js} +4 -4
- package/dist/{chunk-EC2AN74G.js → chunk-IL343FKX.js} +1 -1
- package/dist/{chunk-AG3VC7Y2.js → chunk-J3YQCEP3.js} +2 -2
- package/dist/chunk-NZAYYUQ4.js +172 -0
- package/dist/{chunk-R3Z7W4EX.js → chunk-O7CJJNW6.js} +276 -196
- package/dist/{chunk-MZFJL4X3.js → chunk-OXXXUAWM.js} +1 -1
- package/dist/{chunk-XLI75E3D.js → chunk-QFC6E3WD.js} +2 -2
- package/dist/{chunk-HOTVUVZG.js → chunk-QFGZXWTM.js} +2 -2
- package/dist/{chunk-JC4D7RQ6.js → chunk-QPLFALVV.js} +4 -4
- package/dist/{chunk-FCT6POFX.js → chunk-SALZ3LQB.js} +1 -1
- package/dist/{chunk-CGR3AEZU.js → chunk-TCIBTMJI.js} +68 -2
- package/dist/{chunk-MQN6YDX4.js → chunk-TNFBRXU7.js} +6 -6
- package/dist/{chunk-GVIYGJQG.js → chunk-W5WML3OO.js} +1 -1
- package/dist/chunk-WZ3EJVS6.js +93 -0
- package/dist/{chunk-BHAKNT5M.js → chunk-X25NCWG2.js} +3 -3
- package/dist/{chunk-VFVN7D3A.js → chunk-XFH6SLDK.js} +3 -93
- package/dist/{chunk-HLAUK5GM.js → chunk-Y6M7K4UG.js} +1 -1
- package/dist/{chunk-AVHBWJRY.js → chunk-Z3ABLERJ.js} +3 -1
- package/dist/executions.js +13 -11
- package/dist/history.js +5 -5
- package/dist/index.d.ts +77 -1
- package/dist/index.js +42 -40
- package/dist/{node-detail-view-3VG7XFIZ.js → node-detail-view-WIWW5N6N.js} +31 -7
- package/dist/nodes.js +2 -2
- package/dist/observability.js +6 -5
- package/dist/{quick-config-panel-GB7SDWHD.js → quick-config-panel-O64TXXK3.js} +6 -5
- package/dist/settings.js +6 -5
- package/dist/templates.js +11 -11
- package/dist/workflow-builder.js +13 -13
- package/dist/workflows-list.js +7 -7
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { FailureTriagePanel } from './chunk-
|
|
2
|
-
import {
|
|
1
|
+
import { formatBytes, getFailureBreakdown, getTopFailingWorkflows, getTopFailingNodes, percentage, FailureTriagePanel, formatDuration, prettyJson } from './chunk-TCIBTMJI.js';
|
|
2
|
+
import { aiSpendPeriod, AI_SPEND_RANGE_DAYS, evidenceFacts, monthlySavingLabel, evidenceWindowDays, shortRunId, DISMISS_REASONS, MAX_DISMISS_NOTE_LENGTH, aiCost, formatTokens, runsOfWorkflowPath } from './chunk-NZAYYUQ4.js';
|
|
3
|
+
import { useDataSinks, useDataSinkData, useInsightsData, useRunReplayActions } from './chunk-SALZ3LQB.js';
|
|
3
4
|
import { proposalCanvasPath } from './chunk-GLYINMOR.js';
|
|
4
|
-
import { useContinuousOptimisationEnabled } from './chunk-
|
|
5
|
+
import { useContinuousOptimisationEnabled } from './chunk-3QCRKKTI.js';
|
|
5
6
|
import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS, SdkPermissionButton } from './chunk-5375NUIO.js';
|
|
6
|
-
import { SetContinuousOptimisationEnabledDocument, FgRunOptimisationScanDocument, ProposeOptimisationDocument, WorkflowProposalDocument, DismissOptimisationSuggestionDocument, SnoozeOptimisationSuggestionDocument, OptimisationSuggestionsDocument, ContinuousOptimisationSettingsDocument } from './chunk-
|
|
7
|
+
import { SetContinuousOptimisationEnabledDocument, FgRunOptimisationScanDocument, ProposeOptimisationDocument, WorkflowProposalDocument, DismissOptimisationSuggestionDocument, SnoozeOptimisationSuggestionDocument, WorkspaceAiUsageDocument, OptimisationSuggestionsDocument, ContinuousOptimisationSettingsDocument } from './chunk-Z3ABLERJ.js';
|
|
7
8
|
import { useTr } from './chunk-UULVIT25.js';
|
|
8
9
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
9
10
|
import { throwIfResultError } from './chunk-US5QSLPV.js';
|
|
@@ -12,75 +13,10 @@ import { Icon } from '@iconify/react';
|
|
|
12
13
|
import { Button } from '@burdenoff/fe-libs/button';
|
|
13
14
|
import { Input, Button as Button$1, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Label, Textarea, DialogFooter } from '@burdenoff/fe-libs/ui';
|
|
14
15
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
15
|
-
import { useNavigate } from 'react-router';
|
|
16
|
+
import { useNavigate, Link } from 'react-router';
|
|
16
17
|
import { toast } from 'sonner';
|
|
17
18
|
import { Lightbulb, Scissors, Timer, ShieldCheck, DatabaseZap, BadgeDollarSign, RotateCcw } from 'lucide-react';
|
|
18
19
|
|
|
19
|
-
// src/modules/observability/utils.ts
|
|
20
|
-
function percentage(value, total) {
|
|
21
|
-
if (total <= 0) return 0;
|
|
22
|
-
return Math.round(value / total * 1e3) / 10;
|
|
23
|
-
}
|
|
24
|
-
function getFailureBreakdown(runs) {
|
|
25
|
-
const counts = /* @__PURE__ */ new Map();
|
|
26
|
-
for (const run of runs) {
|
|
27
|
-
const key = run.errorType || "UNKNOWN";
|
|
28
|
-
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
29
|
-
}
|
|
30
|
-
return [...counts.entries()].map(([key, count]) => ({ key, count })).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
31
|
-
}
|
|
32
|
-
function getTopFailingWorkflows(runs) {
|
|
33
|
-
const counts = /* @__PURE__ */ new Map();
|
|
34
|
-
for (const run of runs) {
|
|
35
|
-
const current = counts.get(run.workflowKey);
|
|
36
|
-
if (current) {
|
|
37
|
-
current.count += 1;
|
|
38
|
-
} else {
|
|
39
|
-
counts.set(run.workflowKey, {
|
|
40
|
-
workflowKey: run.workflowKey,
|
|
41
|
-
label: run.workflow?.name || run.workflowKey,
|
|
42
|
-
count: 1
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return [...counts.values()].sort(
|
|
47
|
-
(a, b) => b.count - a.count || a.label.localeCompare(b.label)
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
function getTopFailingNodes(nodeUsageStats) {
|
|
51
|
-
return [...nodeUsageStats].filter((stat) => stat.failureCount > 0).sort(
|
|
52
|
-
(a, b) => b.failureCount - a.failureCount || a.nodeType.localeCompare(b.nodeType)
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
function formatDuration(seconds) {
|
|
56
|
-
if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
|
|
57
|
-
return "\u2014";
|
|
58
|
-
}
|
|
59
|
-
if (seconds < 1) return `${Math.round(seconds * 1e3)}ms`;
|
|
60
|
-
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
61
|
-
const minutes = Math.floor(seconds / 60);
|
|
62
|
-
const remainingSeconds = Math.round(seconds % 60);
|
|
63
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
64
|
-
}
|
|
65
|
-
function formatBytes(bytes) {
|
|
66
|
-
if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
|
|
67
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
68
|
-
const units = ["KB", "MB", "GB", "TB"];
|
|
69
|
-
let value = bytes / 1024;
|
|
70
|
-
let unit = units[0];
|
|
71
|
-
for (let i = 1; i < units.length && value >= 1024; i += 1) {
|
|
72
|
-
value /= 1024;
|
|
73
|
-
unit = units[i];
|
|
74
|
-
}
|
|
75
|
-
return `${value.toFixed(value >= 10 ? 0 : 1)} ${unit}`;
|
|
76
|
-
}
|
|
77
|
-
function prettyJson(value) {
|
|
78
|
-
try {
|
|
79
|
-
return JSON.stringify(value, null, 2) ?? "null";
|
|
80
|
-
} catch {
|
|
81
|
-
return String(value);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
20
|
function valueLabel(value) {
|
|
85
21
|
if (value === null || value === void 0) return "\u2014";
|
|
86
22
|
if (typeof value === "object") return JSON.stringify(value);
|
|
@@ -580,132 +516,6 @@ function useContinuousOptimisation(active) {
|
|
|
580
516
|
};
|
|
581
517
|
}
|
|
582
518
|
|
|
583
|
-
// src/modules/observability/optimisation.ts
|
|
584
|
-
var DEFAULT_WINDOW_DAYS = 30;
|
|
585
|
-
var DISMISS_REASONS = [
|
|
586
|
-
"NOT_APPLICABLE" /* NotApplicable */,
|
|
587
|
-
"ACCEPTED_COST" /* AcceptedCost */,
|
|
588
|
-
"WILL_FIX_LATER" /* WillFixLater */,
|
|
589
|
-
"OTHER" /* Other */
|
|
590
|
-
];
|
|
591
|
-
var MAX_DISMISS_NOTE_LENGTH = 500;
|
|
592
|
-
function isRecord(value) {
|
|
593
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
594
|
-
}
|
|
595
|
-
function evidenceRecord(evidence) {
|
|
596
|
-
if (typeof evidence === "string") {
|
|
597
|
-
try {
|
|
598
|
-
const parsed = JSON.parse(evidence);
|
|
599
|
-
return isRecord(parsed) ? parsed : {};
|
|
600
|
-
} catch {
|
|
601
|
-
return {};
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return isRecord(evidence) ? evidence : {};
|
|
605
|
-
}
|
|
606
|
-
function numberAt(evidence, key) {
|
|
607
|
-
const value = evidence[key];
|
|
608
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
609
|
-
}
|
|
610
|
-
function formatShare(value) {
|
|
611
|
-
return `${Math.round(value * 100)}%`;
|
|
612
|
-
}
|
|
613
|
-
function formatSeconds(ms) {
|
|
614
|
-
const seconds = ms / 1e3;
|
|
615
|
-
return `${Number.isInteger(seconds) ? seconds : seconds.toFixed(1)} s`;
|
|
616
|
-
}
|
|
617
|
-
function formatCount(value) {
|
|
618
|
-
return Math.round(value).toLocaleString("en-US");
|
|
619
|
-
}
|
|
620
|
-
function formatMicroUsd(value) {
|
|
621
|
-
const dollars = value / 1e6;
|
|
622
|
-
if (dollars > 0 && dollars < 0.01) return "< $0.01";
|
|
623
|
-
return `$${dollars.toLocaleString("en-US", {
|
|
624
|
-
minimumFractionDigits: 2,
|
|
625
|
-
maximumFractionDigits: 2
|
|
626
|
-
})}`;
|
|
627
|
-
}
|
|
628
|
-
function monthlySavingLabel(microUsd) {
|
|
629
|
-
if (microUsd === null || microUsd === void 0) return null;
|
|
630
|
-
if (!Number.isFinite(microUsd) || microUsd <= 0) return null;
|
|
631
|
-
return formatMicroUsd(microUsd);
|
|
632
|
-
}
|
|
633
|
-
function evidenceWindowDays(evidence) {
|
|
634
|
-
const days = numberAt(evidenceRecord(evidence), "windowDays");
|
|
635
|
-
return days !== null && days > 0 ? days : DEFAULT_WINDOW_DAYS;
|
|
636
|
-
}
|
|
637
|
-
var FACTS = {
|
|
638
|
-
["RAISE_TIMEOUT" /* RaiseTimeout */]: [
|
|
639
|
-
{ id: "executions", format: formatCount },
|
|
640
|
-
{ id: "timeoutRate", format: formatShare },
|
|
641
|
-
{ id: "currentTimeoutMs", format: formatSeconds },
|
|
642
|
-
{
|
|
643
|
-
id: "p95CompletedMs",
|
|
644
|
-
format: formatSeconds
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
id: "suggestedTimeoutMs",
|
|
648
|
-
format: formatSeconds
|
|
649
|
-
}
|
|
650
|
-
],
|
|
651
|
-
["ADD_RETRY" /* AddRetry */]: [
|
|
652
|
-
{ id: "executions", format: formatCount },
|
|
653
|
-
{
|
|
654
|
-
id: "transientFailures",
|
|
655
|
-
format: formatCount
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
id: "transientFailureRate",
|
|
659
|
-
format: formatShare
|
|
660
|
-
}
|
|
661
|
-
],
|
|
662
|
-
["UNUSED_NODE" /* UnusedNode */]: [{ id: "completedRuns", format: formatCount }],
|
|
663
|
-
["ENABLE_CACHE" /* EnableCache */]: [
|
|
664
|
-
{ id: "calls", format: formatCount },
|
|
665
|
-
{ id: "repeatedCalls", format: formatCount },
|
|
666
|
-
{ id: "repeatShare", format: formatShare },
|
|
667
|
-
{ id: "costMicroUsd", format: formatMicroUsd }
|
|
668
|
-
],
|
|
669
|
-
["CHEAPER_MODEL" /* CheaperModel */]: [],
|
|
670
|
-
["MISSING_GUARDRAIL" /* MissingGuardrail */]: []
|
|
671
|
-
};
|
|
672
|
-
function evidenceFacts(kind, evidence) {
|
|
673
|
-
const record = evidenceRecord(evidence);
|
|
674
|
-
const facts = [];
|
|
675
|
-
for (const spec of FACTS[kind] ?? []) {
|
|
676
|
-
const value = numberAt(record, spec.id);
|
|
677
|
-
if (value === null) continue;
|
|
678
|
-
facts.push({ id: spec.id, value: spec.format(value) });
|
|
679
|
-
}
|
|
680
|
-
if (kind === "UNUSED_NODE" /* UnusedNode */) {
|
|
681
|
-
const removed = record.removedNodeIds;
|
|
682
|
-
if (Array.isArray(removed) && removed.length > 0) {
|
|
683
|
-
facts.push({
|
|
684
|
-
id: "removedNodeIds",
|
|
685
|
-
value: removed.map(String).join(", ")
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
if (kind === "ADD_RETRY" /* AddRetry */) {
|
|
690
|
-
const byType = record.failuresByType;
|
|
691
|
-
if (isRecord(byType)) {
|
|
692
|
-
const parts = Object.entries(byType).filter(
|
|
693
|
-
(entry) => typeof entry[1] === "number" && entry[1] > 0
|
|
694
|
-
).map(([type, count]) => `${type} ${formatCount(count)}`);
|
|
695
|
-
if (parts.length > 0) {
|
|
696
|
-
facts.push({
|
|
697
|
-
id: "failuresByType",
|
|
698
|
-
value: parts.join(", ")
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return facts;
|
|
704
|
-
}
|
|
705
|
-
function shortRunId(runId) {
|
|
706
|
-
return runId.length > 8 ? runId.slice(0, 8) : runId;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
519
|
// src/modules/observability/optimisation-labels.ts
|
|
710
520
|
function kindLabel(tr, kind) {
|
|
711
521
|
switch (kind) {
|
|
@@ -1339,6 +1149,273 @@ function DismissDialog({
|
|
|
1339
1149
|
}
|
|
1340
1150
|
);
|
|
1341
1151
|
}
|
|
1152
|
+
async function fetchWorkspaceAiUsage(from, to) {
|
|
1153
|
+
const result = await getFluidGridsClient().query({
|
|
1154
|
+
query: WorkspaceAiUsageDocument,
|
|
1155
|
+
variables: { from, to },
|
|
1156
|
+
fetchPolicy: "network-only"
|
|
1157
|
+
});
|
|
1158
|
+
throwIfResultError(result);
|
|
1159
|
+
return result.data?.workspaceAiUsage ?? null;
|
|
1160
|
+
}
|
|
1161
|
+
function useWorkspaceAiUsage(days) {
|
|
1162
|
+
const [sent, setSent] = useState(0);
|
|
1163
|
+
const [settled, setSettled] = useState(null);
|
|
1164
|
+
const request = useRef(0);
|
|
1165
|
+
const read = useCallback(async () => {
|
|
1166
|
+
const id = ++request.current;
|
|
1167
|
+
setSent(id);
|
|
1168
|
+
const { from, to } = aiSpendPeriod(days);
|
|
1169
|
+
let next;
|
|
1170
|
+
try {
|
|
1171
|
+
next = { usage: await fetchWorkspaceAiUsage(from, to), error: null };
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
next = {
|
|
1174
|
+
usage: null,
|
|
1175
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
if (id !== request.current) return;
|
|
1179
|
+
setSettled({ request: id, ...next });
|
|
1180
|
+
}, [days]);
|
|
1181
|
+
useEffect(() => {
|
|
1182
|
+
void read();
|
|
1183
|
+
}, [read]);
|
|
1184
|
+
const loading = (settled?.request ?? -1) !== sent || sent === 0;
|
|
1185
|
+
return {
|
|
1186
|
+
usage: settled?.usage ?? null,
|
|
1187
|
+
loading,
|
|
1188
|
+
error: settled && settled.request === sent ? settled.error : null,
|
|
1189
|
+
refetch: read
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
function rangeLabel(days, tr) {
|
|
1193
|
+
switch (days) {
|
|
1194
|
+
case 7:
|
|
1195
|
+
return tr("fluidgrids.insights.aiSpend.range7", "7 days");
|
|
1196
|
+
case 30:
|
|
1197
|
+
return tr("fluidgrids.insights.aiSpend.range30", "30 days");
|
|
1198
|
+
case 90:
|
|
1199
|
+
return tr("fluidgrids.insights.aiSpend.range90", "90 days");
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
function Cost({ usage, tr }) {
|
|
1203
|
+
const cost = aiCost(usage);
|
|
1204
|
+
if (cost.kind === "priced") return /* @__PURE__ */ jsx(Fragment, { children: cost.text });
|
|
1205
|
+
if (cost.kind === "unpriced") {
|
|
1206
|
+
return /* @__PURE__ */ jsx("span", { className: "text-text-secondary", children: tr("fluidgrids.insights.aiSpend.notPricedYet", "Not priced yet") });
|
|
1207
|
+
}
|
|
1208
|
+
return /* @__PURE__ */ jsx(Fragment, { children: "\u2014" });
|
|
1209
|
+
}
|
|
1210
|
+
function AiSpendPanel() {
|
|
1211
|
+
const tr = useTr();
|
|
1212
|
+
const [days, setDays] = useState(7);
|
|
1213
|
+
const { usage, loading, error, refetch } = useWorkspaceAiUsage(days);
|
|
1214
|
+
return /* @__PURE__ */ jsxs(
|
|
1215
|
+
"section",
|
|
1216
|
+
{
|
|
1217
|
+
className: "rounded-xl border border-border-secondary bg-background-secondary p-4 sm:p-5",
|
|
1218
|
+
"aria-label": tr("fluidgrids.insights.aiSpend.title", "AI spend"),
|
|
1219
|
+
"data-testid": "insights-ai-spend",
|
|
1220
|
+
children: [
|
|
1221
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
|
|
1222
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1223
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base font-semibold text-text-primary", children: tr("fluidgrids.insights.aiSpend.title", "AI spend") }),
|
|
1224
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-text-secondary", children: tr(
|
|
1225
|
+
"fluidgrids.insights.aiSpend.subtitle",
|
|
1226
|
+
"Model calls made by your workflow runs, most expensive workflow first."
|
|
1227
|
+
) })
|
|
1228
|
+
] }),
|
|
1229
|
+
/* @__PURE__ */ jsx(RangeSelector, { days, onChange: setDays, tr })
|
|
1230
|
+
] }),
|
|
1231
|
+
/* @__PURE__ */ jsx(
|
|
1232
|
+
AiSpendBody,
|
|
1233
|
+
{
|
|
1234
|
+
usage,
|
|
1235
|
+
loading,
|
|
1236
|
+
error,
|
|
1237
|
+
onRetry: () => void refetch(),
|
|
1238
|
+
tr
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
]
|
|
1242
|
+
}
|
|
1243
|
+
);
|
|
1244
|
+
}
|
|
1245
|
+
function RangeSelector({
|
|
1246
|
+
days,
|
|
1247
|
+
onChange,
|
|
1248
|
+
tr
|
|
1249
|
+
}) {
|
|
1250
|
+
return /* @__PURE__ */ jsx(
|
|
1251
|
+
"div",
|
|
1252
|
+
{
|
|
1253
|
+
className: "flex shrink-0 gap-1 rounded-lg border border-border-secondary p-1",
|
|
1254
|
+
role: "group",
|
|
1255
|
+
"aria-label": tr("fluidgrids.insights.aiSpend.rangeLabel", "Period"),
|
|
1256
|
+
children: AI_SPEND_RANGE_DAYS.map((option) => /* @__PURE__ */ jsx(
|
|
1257
|
+
"button",
|
|
1258
|
+
{
|
|
1259
|
+
type: "button",
|
|
1260
|
+
"aria-pressed": days === option,
|
|
1261
|
+
onClick: () => onChange(option),
|
|
1262
|
+
className: `rounded px-3 py-1 text-sm ${days === option ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:text-text-primary"}`,
|
|
1263
|
+
children: rangeLabel(option, tr)
|
|
1264
|
+
},
|
|
1265
|
+
option
|
|
1266
|
+
))
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
function AiSpendBody({
|
|
1271
|
+
usage,
|
|
1272
|
+
loading,
|
|
1273
|
+
error,
|
|
1274
|
+
onRetry,
|
|
1275
|
+
tr
|
|
1276
|
+
}) {
|
|
1277
|
+
if (error) {
|
|
1278
|
+
return /* @__PURE__ */ jsxs(
|
|
1279
|
+
"div",
|
|
1280
|
+
{
|
|
1281
|
+
role: "alert",
|
|
1282
|
+
className: "mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3 text-sm text-status-error-text",
|
|
1283
|
+
children: [
|
|
1284
|
+
/* @__PURE__ */ jsx("p", { children: tr(
|
|
1285
|
+
"fluidgrids.insights.aiSpend.loadError",
|
|
1286
|
+
"Could not load AI spend: {{message}}",
|
|
1287
|
+
{ message: error }
|
|
1288
|
+
) }),
|
|
1289
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "mt-2", onClick: onRetry, children: tr("fluidgrids.insights.aiSpend.retry", "Try again") })
|
|
1290
|
+
]
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
if (!usage) {
|
|
1295
|
+
return loading ? /* @__PURE__ */ jsx("div", { className: "mt-4 grid gap-3 sm:grid-cols-3", "aria-busy": "true", children: [1, 2, 3].map((item) => /* @__PURE__ */ jsx(
|
|
1296
|
+
"div",
|
|
1297
|
+
{
|
|
1298
|
+
className: "h-20 animate-pulse rounded-lg bg-background-tertiary"
|
|
1299
|
+
},
|
|
1300
|
+
item
|
|
1301
|
+
)) }) : null;
|
|
1302
|
+
}
|
|
1303
|
+
return /* @__PURE__ */ jsxs("div", { className: loading ? "opacity-60" : void 0, "aria-busy": loading, children: [
|
|
1304
|
+
/* @__PURE__ */ jsx(SpendTotals, { total: usage.total, tr }),
|
|
1305
|
+
usage.lines.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-4 rounded-lg border border-dashed border-border-secondary p-6 text-center", children: [
|
|
1306
|
+
/* @__PURE__ */ jsx(
|
|
1307
|
+
Icon,
|
|
1308
|
+
{
|
|
1309
|
+
icon: "mdi:robot-off-outline",
|
|
1310
|
+
width: 28,
|
|
1311
|
+
className: "mx-auto text-text-secondary"
|
|
1312
|
+
}
|
|
1313
|
+
),
|
|
1314
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-text-secondary", children: tr(
|
|
1315
|
+
"fluidgrids.insights.aiSpend.empty",
|
|
1316
|
+
"No workflow run made a model call in this period."
|
|
1317
|
+
) })
|
|
1318
|
+
] }) : /* @__PURE__ */ jsx(SpendTable, { lines: usage.lines, tr }),
|
|
1319
|
+
usage.truncated ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-xs text-text-secondary", children: tr(
|
|
1320
|
+
"fluidgrids.insights.aiSpend.truncated",
|
|
1321
|
+
"Showing the top {{count}} workflows by spend; the totals above include every workflow.",
|
|
1322
|
+
{ count: usage.lines.length }
|
|
1323
|
+
) }) : null
|
|
1324
|
+
] });
|
|
1325
|
+
}
|
|
1326
|
+
function SpendTotals({ total, tr }) {
|
|
1327
|
+
const totalCost = aiCost(total);
|
|
1328
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-4 grid gap-3 sm:grid-cols-3", children: [
|
|
1329
|
+
/* @__PURE__ */ jsx(
|
|
1330
|
+
Total,
|
|
1331
|
+
{
|
|
1332
|
+
label: tr("fluidgrids.insights.aiSpend.totalCost", "Estimated cost"),
|
|
1333
|
+
detail: totalCost.kind === "priced" && totalCost.partial ? tr(
|
|
1334
|
+
"fluidgrids.insights.aiSpend.partialCost",
|
|
1335
|
+
"Calls not priced yet: {{count}}",
|
|
1336
|
+
{ count: totalCost.unpricedCalls }
|
|
1337
|
+
) : null,
|
|
1338
|
+
children: /* @__PURE__ */ jsx(Cost, { usage: total, tr })
|
|
1339
|
+
}
|
|
1340
|
+
),
|
|
1341
|
+
/* @__PURE__ */ jsx(
|
|
1342
|
+
Total,
|
|
1343
|
+
{
|
|
1344
|
+
label: tr("fluidgrids.insights.aiSpend.totalCalls", "Model calls"),
|
|
1345
|
+
children: total.calls.toLocaleString("en-US")
|
|
1346
|
+
}
|
|
1347
|
+
),
|
|
1348
|
+
/* @__PURE__ */ jsxs(
|
|
1349
|
+
Total,
|
|
1350
|
+
{
|
|
1351
|
+
label: tr("fluidgrids.insights.aiSpend.totalTokens", "Tokens in / out"),
|
|
1352
|
+
detail: total.cachedInputTokens > 0 ? tr(
|
|
1353
|
+
"fluidgrids.insights.aiSpend.cachedTokens",
|
|
1354
|
+
"{{count}} input tokens cached",
|
|
1355
|
+
{ count: formatTokens(total.cachedInputTokens) }
|
|
1356
|
+
) : null,
|
|
1357
|
+
children: [
|
|
1358
|
+
formatTokens(total.inputTokens),
|
|
1359
|
+
" / ",
|
|
1360
|
+
formatTokens(total.outputTokens)
|
|
1361
|
+
]
|
|
1362
|
+
}
|
|
1363
|
+
)
|
|
1364
|
+
] });
|
|
1365
|
+
}
|
|
1366
|
+
function Total({
|
|
1367
|
+
label,
|
|
1368
|
+
children,
|
|
1369
|
+
detail
|
|
1370
|
+
}) {
|
|
1371
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border-secondary bg-background-primary p-3", children: [
|
|
1372
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-text-secondary", children: label }),
|
|
1373
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xl font-semibold text-text-primary break-words", children }),
|
|
1374
|
+
detail && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs text-text-secondary", children: detail })
|
|
1375
|
+
] });
|
|
1376
|
+
}
|
|
1377
|
+
function SpendTable({
|
|
1378
|
+
lines,
|
|
1379
|
+
tr
|
|
1380
|
+
}) {
|
|
1381
|
+
return /* @__PURE__ */ jsx("div", { className: "mt-4 overflow-x-auto rounded-lg border border-border-secondary", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-[640px] text-sm", children: [
|
|
1382
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-background-tertiary text-left text-xs text-text-secondary", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1383
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 font-medium", children: tr("fluidgrids.insights.aiSpend.colWorkflow", "Workflow") }),
|
|
1384
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr("fluidgrids.insights.aiSpend.colRuns", "Runs") }),
|
|
1385
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr("fluidgrids.insights.aiSpend.colCalls", "Calls") }),
|
|
1386
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr("fluidgrids.insights.aiSpend.colTokensIn", "Tokens in") }),
|
|
1387
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr("fluidgrids.insights.aiSpend.colTokensOut", "Tokens out") }),
|
|
1388
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-2 text-right font-medium", children: tr("fluidgrids.insights.aiSpend.colCost", "Cost") })
|
|
1389
|
+
] }) }),
|
|
1390
|
+
/* @__PURE__ */ jsx("tbody", { children: lines.map((line) => /* @__PURE__ */ jsxs(
|
|
1391
|
+
"tr",
|
|
1392
|
+
{
|
|
1393
|
+
className: "border-t border-border-secondary",
|
|
1394
|
+
children: [
|
|
1395
|
+
/* @__PURE__ */ jsx("td", { className: "max-w-[16rem] px-3 py-2", children: /* @__PURE__ */ jsx(
|
|
1396
|
+
Link,
|
|
1397
|
+
{
|
|
1398
|
+
to: runsOfWorkflowPath(line.workflowKey),
|
|
1399
|
+
className: "block truncate font-mono text-text-link underline-offset-2 hover:text-text-linkHover hover:underline",
|
|
1400
|
+
title: tr(
|
|
1401
|
+
"fluidgrids.insights.aiSpend.openRuns",
|
|
1402
|
+
"Open the runs of {{workflow}}",
|
|
1403
|
+
{ workflow: line.workflowKey }
|
|
1404
|
+
),
|
|
1405
|
+
children: line.workflowKey
|
|
1406
|
+
}
|
|
1407
|
+
) }),
|
|
1408
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: line.runs.toLocaleString("en-US") }),
|
|
1409
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: line.usage.calls.toLocaleString("en-US") }),
|
|
1410
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: formatTokens(line.usage.inputTokens) }),
|
|
1411
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right text-text-primary", children: formatTokens(line.usage.outputTokens) }),
|
|
1412
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-right font-medium text-text-primary", children: /* @__PURE__ */ jsx(Cost, { usage: line.usage, tr }) })
|
|
1413
|
+
]
|
|
1414
|
+
},
|
|
1415
|
+
line.workflowKey
|
|
1416
|
+
)) })
|
|
1417
|
+
] }) });
|
|
1418
|
+
}
|
|
1342
1419
|
function normalizedRate(summary) {
|
|
1343
1420
|
const supplied = summary.workflowSuccessRate;
|
|
1344
1421
|
if (supplied !== null && supplied !== void 0) {
|
|
@@ -1527,6 +1604,9 @@ function InsightsPage({ showErrors = false }) {
|
|
|
1527
1604
|
)
|
|
1528
1605
|
}
|
|
1529
1606
|
),
|
|
1607
|
+
activeTab === "overview" && // AI spend (BOFF-9937): its own query and period, so it loads and
|
|
1608
|
+
// fails independently of the run summary below.
|
|
1609
|
+
/* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(AiSpendPanel, {}) }),
|
|
1530
1610
|
loading && !summary ? /* @__PURE__ */ jsx(LoadingState, {}) : activeTab === "overview" && summary ? /* @__PURE__ */ jsx(
|
|
1531
1611
|
OverviewContent,
|
|
1532
1612
|
{
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeEndpointConfigsDocument, NodeEndpointConfigDocument, NodeManifestDocument, fetchAllNodeEndpointConfigs } from './chunk-
|
|
1
|
+
import { NodeEndpointConfigsDocument, NodeEndpointConfigDocument, NodeManifestDocument, fetchAllNodeEndpointConfigs } from './chunk-Z3ABLERJ.js';
|
|
2
2
|
import { useTr } from './chunk-UULVIT25.js';
|
|
3
3
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
4
4
|
import { useMemo, useState, useCallback, useEffect } from 'react';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { signalPayloadCard, components_default, payloadFromCardValues, parseRawSignalPayload } from './chunk-
|
|
2
|
-
import { TerminateWorkflowRunDocument, WorkflowRunHistoryDocument, RunEventAppendedDocument, DurableRunFieldsDocument, WorkflowRunDeterminismStatusDocument, WorkflowRunStackTraceDocument, SignalWorkflowRunDocument } from './chunk-
|
|
1
|
+
import { signalPayloadCard, components_default, payloadFromCardValues, parseRawSignalPayload } from './chunk-3QCRKKTI.js';
|
|
2
|
+
import { TerminateWorkflowRunDocument, WorkflowRunHistoryDocument, RunEventAppendedDocument, DurableRunFieldsDocument, WorkflowRunDeterminismStatusDocument, WorkflowRunStackTraceDocument, SignalWorkflowRunDocument } from './chunk-Z3ABLERJ.js';
|
|
3
3
|
import { useTr } from './chunk-UULVIT25.js';
|
|
4
4
|
import { getFluidGridsClient, getFluidGridsClientSafe } from './chunk-3IXU7WUC.js';
|
|
5
5
|
import { throwIfResultError, describeOperationError } from './chunk-US5QSLPV.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useFluidGridsAnalytics, emitWorkflowCreated, emitRunStarted, runTriggerFromMode, openRunQuotaUpgradePrompt, emitWorkflowPublished } from './chunk-
|
|
2
|
-
import { WorkflowDefinitionsDocument, WorkflowDefinitionDocument, WorkflowVersionsDocument, CreateWorkflowDefinitionDocument, ImportWorkflowDocument, PreviewN8nImportDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, DeleteWorkflowDefinitionDocument, DuplicateWorkflowDocument, RestoreWorkflowVersionDocument, TriggerWorkflowDocument, WorkflowRunsDocument, WorkflowRunDocument, DashboardSummaryDocument, ActivateWorkflowDefinitionDocument, DeactivateWorkflowDefinitionDocument } from './chunk-
|
|
1
|
+
import { useFluidGridsAnalytics, emitWorkflowCreated, emitRunStarted, runTriggerFromMode, openRunQuotaUpgradePrompt, emitWorkflowPublished } from './chunk-3QCRKKTI.js';
|
|
2
|
+
import { WorkflowDefinitionsDocument, WorkflowDefinitionDocument, WorkflowVersionsDocument, CreateWorkflowDefinitionDocument, ImportWorkflowDocument, PreviewN8nImportDocument, UpdateWorkflowDefinitionDocument, CreateWorkflowVersionDocument, DeleteWorkflowDefinitionDocument, DuplicateWorkflowDocument, RestoreWorkflowVersionDocument, TriggerWorkflowDocument, WorkflowRunsDocument, WorkflowRunDocument, DashboardSummaryDocument, ActivateWorkflowDefinitionDocument, DeactivateWorkflowDefinitionDocument } from './chunk-Z3ABLERJ.js';
|
|
3
3
|
import { useTr } from './chunk-UULVIT25.js';
|
|
4
4
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
5
5
|
import { throwIfResultError, emitFluidGridsEvent, emitOperationErrorEvent } from './chunk-US5QSLPV.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { AgentsTab } from './chunk-
|
|
1
|
+
import { AgentsTab } from './chunk-XFH6SLDK.js';
|
|
2
2
|
import { ActionPoliciesTab } from './chunk-AMXJLVZN.js';
|
|
3
|
-
import { LogStreamingTab } from './chunk-
|
|
3
|
+
import { LogStreamingTab } from './chunk-SALZ3LQB.js';
|
|
4
4
|
import { FluidGridsDocsLink } from './chunk-4VJ3MDOV.js';
|
|
5
|
-
import { useFlowStore, useFluidGridsFlag, FLUIDGRIDS_FLAGS, useSaveStatusStore, findSaveConflict, useDurableParityEnabled, useConnections } from './chunk-
|
|
5
|
+
import { useFlowStore, useFluidGridsFlag, FLUIDGRIDS_FLAGS, useSaveStatusStore, findSaveConflict, useDurableParityEnabled, useConnections } from './chunk-3QCRKKTI.js';
|
|
6
6
|
import { useFluidGridsPermission, FLUIDGRIDS_PERMISSIONS } from './chunk-5375NUIO.js';
|
|
7
7
|
import { getGlobalStoreClient } from './chunk-AU7LACOY.js';
|
|
8
|
-
import { useWorkflowDefinitionQuery, useUpdateWorkflowDefinitionMutation, useWorkflowDefinitionsQuery, DurableHistoryCapsDocument, UpdateDurableHistoryCapsDocument } from './chunk-
|
|
8
|
+
import { useWorkflowDefinitionQuery, useUpdateWorkflowDefinitionMutation, useWorkflowDefinitionsQuery, DurableHistoryCapsDocument, UpdateDurableHistoryCapsDocument } from './chunk-Z3ABLERJ.js';
|
|
9
9
|
import { useTr } from './chunk-UULVIT25.js';
|
|
10
10
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
11
11
|
import { throwIfResultError, describeOperationError } from './chunk-US5QSLPV.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LOG_STREAM_DESTINATIONS, CREATE_LOG_STREAM_DESTINATION, UPDATE_LOG_STREAM_DESTINATION, DELETE_LOG_STREAM_DESTINATION, TEST_LOG_STREAM_DESTINATION, REPLAY_WORKFLOW_RUN, RETRY_WORKFLOW_RUNS, OBSERVABILITY_DASHBOARD_SUMMARY, OBSERVABILITY_FAILED_RUNS, OBSERVABILITY_DATA_SINKS, OBSERVABILITY_DATA_SINK_DATA } from './chunk-5ERAJC2I.js';
|
|
2
|
-
import { useFluidGridsAnalytics, emitRunStarted, openRunQuotaUpgradePrompt, useFluidGridsFlag, FLUIDGRIDS_FLAGS } from './chunk-
|
|
2
|
+
import { useFluidGridsAnalytics, emitRunStarted, openRunQuotaUpgradePrompt, useFluidGridsFlag, FLUIDGRIDS_FLAGS } from './chunk-3QCRKKTI.js';
|
|
3
3
|
import { useTr } from './chunk-UULVIT25.js';
|
|
4
4
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
5
5
|
import { throwIfResultError, describeOperationError, emitFluidGridsEvent } from './chunk-US5QSLPV.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RunFailureClustersDocument, DismissRunFailureClusterDocument, RunFailureTriageSettingsDocument, SetRunFailureTriageEnabledDocument, FgTriageFailedRunsDocument } from './chunk-
|
|
1
|
+
import { RunFailureClustersDocument, DismissRunFailureClusterDocument, RunFailureTriageSettingsDocument, SetRunFailureTriageEnabledDocument, FgTriageFailedRunsDocument } from './chunk-Z3ABLERJ.js';
|
|
2
2
|
import { useTr } from './chunk-UULVIT25.js';
|
|
3
3
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
4
4
|
import { throwIfResultError } from './chunk-US5QSLPV.js';
|
|
@@ -325,4 +325,70 @@ function FailureTriagePanel({
|
|
|
325
325
|
);
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
|
|
328
|
+
// src/modules/observability/utils.ts
|
|
329
|
+
function percentage(value, total) {
|
|
330
|
+
if (total <= 0) return 0;
|
|
331
|
+
return Math.round(value / total * 1e3) / 10;
|
|
332
|
+
}
|
|
333
|
+
function getFailureBreakdown(runs) {
|
|
334
|
+
const counts = /* @__PURE__ */ new Map();
|
|
335
|
+
for (const run of runs) {
|
|
336
|
+
const key = run.errorType || "UNKNOWN";
|
|
337
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
338
|
+
}
|
|
339
|
+
return [...counts.entries()].map(([key, count]) => ({ key, count })).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
340
|
+
}
|
|
341
|
+
function getTopFailingWorkflows(runs) {
|
|
342
|
+
const counts = /* @__PURE__ */ new Map();
|
|
343
|
+
for (const run of runs) {
|
|
344
|
+
const current = counts.get(run.workflowKey);
|
|
345
|
+
if (current) {
|
|
346
|
+
current.count += 1;
|
|
347
|
+
} else {
|
|
348
|
+
counts.set(run.workflowKey, {
|
|
349
|
+
workflowKey: run.workflowKey,
|
|
350
|
+
label: run.workflow?.name || run.workflowKey,
|
|
351
|
+
count: 1
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return [...counts.values()].sort(
|
|
356
|
+
(a, b) => b.count - a.count || a.label.localeCompare(b.label)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
function getTopFailingNodes(nodeUsageStats) {
|
|
360
|
+
return [...nodeUsageStats].filter((stat) => stat.failureCount > 0).sort(
|
|
361
|
+
(a, b) => b.failureCount - a.failureCount || a.nodeType.localeCompare(b.nodeType)
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
function formatDuration(seconds) {
|
|
365
|
+
if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
|
|
366
|
+
return "\u2014";
|
|
367
|
+
}
|
|
368
|
+
if (seconds < 1) return `${Math.round(seconds * 1e3)}ms`;
|
|
369
|
+
if (seconds < 60) return `${seconds.toFixed(1)}s`;
|
|
370
|
+
const minutes = Math.floor(seconds / 60);
|
|
371
|
+
const remainingSeconds = Math.round(seconds % 60);
|
|
372
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
373
|
+
}
|
|
374
|
+
function formatBytes(bytes) {
|
|
375
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "\u2014";
|
|
376
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
377
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
378
|
+
let value = bytes / 1024;
|
|
379
|
+
let unit = units[0];
|
|
380
|
+
for (let i = 1; i < units.length && value >= 1024; i += 1) {
|
|
381
|
+
value /= 1024;
|
|
382
|
+
unit = units[i];
|
|
383
|
+
}
|
|
384
|
+
return `${value.toFixed(value >= 10 ? 0 : 1)} ${unit}`;
|
|
385
|
+
}
|
|
386
|
+
function prettyJson(value) {
|
|
387
|
+
try {
|
|
388
|
+
return JSON.stringify(value, null, 2) ?? "null";
|
|
389
|
+
} catch {
|
|
390
|
+
return String(value);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export { BUCKET_LABELS, FailureTriagePanel, OPEN_STATUSES, dismissRunFailureCluster, fetchRunFailureClusters, formatBytes, formatDuration, getFailureBreakdown, getTopFailingNodes, getTopFailingWorkflows, percentage, prettyJson };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isGuidedTourIntent, pickGuidedTour, withoutGuidedTourIntent, isCreateIntent, CreateWorkflowRefusalDialog, decideMissingWorkflow } from './chunk-
|
|
1
|
+
import { isGuidedTourIntent, pickGuidedTour, withoutGuidedTourIntent, isCreateIntent, CreateWorkflowRefusalDialog, decideMissingWorkflow } from './chunk-Y6M7K4UG.js';
|
|
2
2
|
import { registerLiveCanvasHandler } from './chunk-PIKYQE7P.js';
|
|
3
|
-
import { useWorkflowReloadStore, releaseLeaseKeptForReload, useInstalledNodes, buildNodeCheck, FlowBuilderPage, buildRequiredNodeTypes } from './chunk-
|
|
4
|
-
import { canvasFromDefinition } from './chunk-
|
|
5
|
-
import { useLazyWorkflowDefinition, useCreateWorkflow } from './chunk-
|
|
6
|
-
import { useFlowStore, refetchStoreNodes, BILLING_PLANS_PATH, codeBuildIdOf, mergeStickyNotes, rememberRevision, blankWorkflowCreateInput, classifyCreateRefusal, useSaveStatusStore, workflowFingerprint, registerWorkflowNodeTypes } from './chunk-
|
|
7
|
-
import { WorkflowDefinitionDocument } from './chunk-
|
|
3
|
+
import { useWorkflowReloadStore, releaseLeaseKeptForReload, useInstalledNodes, buildNodeCheck, FlowBuilderPage, buildRequiredNodeTypes } from './chunk-62CCBMDC.js';
|
|
4
|
+
import { canvasFromDefinition } from './chunk-64JN2TVX.js';
|
|
5
|
+
import { useLazyWorkflowDefinition, useCreateWorkflow } from './chunk-QFGZXWTM.js';
|
|
6
|
+
import { useFlowStore, refetchStoreNodes, BILLING_PLANS_PATH, codeBuildIdOf, mergeStickyNotes, rememberRevision, blankWorkflowCreateInput, classifyCreateRefusal, useSaveStatusStore, workflowFingerprint, registerWorkflowNodeTypes } from './chunk-3QCRKKTI.js';
|
|
7
|
+
import { WorkflowDefinitionDocument } from './chunk-Z3ABLERJ.js';
|
|
8
8
|
import { useTr } from './chunk-UULVIT25.js';
|
|
9
9
|
import { getFluidGridsClient } from './chunk-3IXU7WUC.js';
|
|
10
10
|
import { throwIfResultError } from './chunk-US5QSLPV.js';
|