@acarmisc/backstage-plugin-litellm 0.29.0 → 0.31.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/dist/index.cjs.js CHANGED
@@ -1060,16 +1060,21 @@ var init_Gauge = __esm({
1060
1060
  fontSize: size <= 64 ? 15 : 18,
1061
1061
  fontWeight: 700,
1062
1062
  fontVariantNumeric: "tabular-nums",
1063
- color: "text.primary"
1063
+ color: isOverCap ? "currentColor" : "text.primary"
1064
1064
  },
1065
- children: isOverCap ? "Over cap" : label ?? `${Math.round(value)}%`
1065
+ children: label ?? `${Math.round(value)}%`
1066
1066
  }
1067
1067
  ),
1068
1068
  caption && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1069
1069
  import_Typography3.default,
1070
1070
  {
1071
1071
  variant: "caption",
1072
- sx: { fontSize: 9.5, color: "text.secondary", mt: 0.25 },
1072
+ sx: {
1073
+ fontSize: 9.5,
1074
+ color: "text.secondary",
1075
+ mt: 0.25,
1076
+ whiteSpace: "nowrap"
1077
+ },
1073
1078
  children: caption
1074
1079
  }
1075
1080
  )
@@ -1524,6 +1529,18 @@ function pruneCopy(n) {
1524
1529
  }
1525
1530
  return `Remove ${n} expired keys from the list. They already don't work.`;
1526
1531
  }
1532
+ function blockButtonState(blocked, manage, unblock) {
1533
+ if (blocked) {
1534
+ return {
1535
+ label: unblock.denied ? "No permission to unblock keys" : "Unblock key",
1536
+ disabled: !unblock.allowed
1537
+ };
1538
+ }
1539
+ return {
1540
+ label: manage.denied ? "No permission to block keys" : "Block key \u2014 suspends without revoking",
1541
+ disabled: !manage.allowed
1542
+ };
1543
+ }
1527
1544
  var init_keyLabels = __esm({
1528
1545
  "src/keyLabels.ts"() {
1529
1546
  "use strict";
@@ -1738,10 +1755,13 @@ var init_KeysTable = __esm({
1738
1755
  }) => {
1739
1756
  const managePermission = (0, import_plugin_permission_react.usePermission)({ permission: import_backstage_plugin_litellm_common.litellmKeyManagePermission });
1740
1757
  const revokePermission = (0, import_plugin_permission_react.usePermission)({ permission: import_backstage_plugin_litellm_common.litellmKeyRevokePermission });
1758
+ const unblockPermission = (0, import_plugin_permission_react.usePermission)({ permission: import_backstage_plugin_litellm_common.litellmKeyUnblockPermission });
1741
1759
  const canManageKeys = !managePermission.loading && managePermission.allowed;
1742
1760
  const canRevokeKeys = !revokePermission.loading && revokePermission.allowed;
1743
1761
  const manageDenied = !managePermission.loading && !managePermission.allowed;
1744
1762
  const revokeDenied = !revokePermission.loading && !revokePermission.allowed;
1763
+ const canUnblockKeys = !unblockPermission.loading && unblockPermission.allowed;
1764
+ const unblockDenied = !unblockPermission.loading && !unblockPermission.allowed;
1745
1765
  const [blockConfirmKey, setBlockConfirmKey] = (0, import_react4.useState)(null);
1746
1766
  const [blockSubmitting, setBlockSubmitting] = (0, import_react4.useState)(false);
1747
1767
  const [deleteConfirmKey, setDeleteConfirmKey] = (0, import_react4.useState)(null);
@@ -1860,6 +1880,11 @@ var init_KeysTable = __esm({
1860
1880
  return filteredKeys.map((key) => {
1861
1881
  const keyId = key.token ?? key.key;
1862
1882
  const keyModels = key.models ?? [];
1883
+ const blockState = blockButtonState(
1884
+ key.blocked,
1885
+ { allowed: canManageKeys, denied: manageDenied },
1886
+ { allowed: canUnblockKeys, denied: unblockDenied }
1887
+ );
1863
1888
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1864
1889
  import_TableRow.default,
1865
1890
  {
@@ -1931,19 +1956,13 @@ var init_KeysTable = __esm({
1931
1956
  children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_icons_material2.Edit, { fontSize: "small" })
1932
1957
  }
1933
1958
  ) }) }),
1934
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_Tooltip.default, { describeChild: true, title: (() => {
1935
- if (manageDenied) return "No permission to block keys";
1936
- return key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking";
1937
- })(), placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1959
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_Tooltip.default, { describeChild: true, title: blockState.label, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1938
1960
  import_IconButton.default,
1939
1961
  {
1940
1962
  size: "small",
1941
1963
  onClick: () => handleToggleBlock(key),
1942
- disabled: blockSubmitting || !canManageKeys,
1943
- "aria-label": (() => {
1944
- if (manageDenied) return "No permission to block keys";
1945
- return key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking";
1946
- })(),
1964
+ disabled: blockSubmitting || blockState.disabled,
1965
+ "aria-label": blockState.label,
1947
1966
  sx: quietIconButtonSx("warning"),
1948
1967
  children: keyBlockIcon(blockSubmitting, key.blocked)
1949
1968
  }
@@ -6563,6 +6582,7 @@ init_widgetState();
6563
6582
  // src/homeWidgetHelpers.ts
6564
6583
  init_format();
6565
6584
  init_tokens();
6585
+ init_dates();
6566
6586
  function rangeCaption(startDay, endDay) {
6567
6587
  return `${fmtDateShort(startDay)} \u2013 ${fmtDateShort(endDay)}`;
6568
6588
  }
@@ -6587,6 +6607,10 @@ function sparklineAriaLabel(points) {
6587
6607
  const peakDate = fmtDateShort(maxPoint.date);
6588
6608
  return `Daily spend over ${dayCount} day${dayCount === 1 ? "" : "s"}, total ${fmtUsd(total)}, peak ${fmtUsd(maxPoint.spend)} on ${peakDate}`;
6589
6609
  }
6610
+ function spendOnDay(points, now = /* @__PURE__ */ new Date()) {
6611
+ const day = toLocalDay(now);
6612
+ return points.find((p) => p.date === day)?.spend ?? 0;
6613
+ }
6590
6614
  function usageSummary(usage) {
6591
6615
  if (!usage) {
6592
6616
  return "$0.00 spent \xB7 0 in \xB7 0 out";
@@ -6815,7 +6839,76 @@ function resetLabel(limit) {
6815
6839
  const window2 = fmtBudgetDuration(limit.budgetDuration);
6816
6840
  return window2 ? `resets ${window2}` : "never resets";
6817
6841
  }
6818
- var LevelGauge = ({
6842
+ var Tag = ({ children, tone, title }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6843
+ import_Typography17.default,
6844
+ {
6845
+ title,
6846
+ sx: (theme) => ({
6847
+ fontSize: 10.5,
6848
+ fontWeight: 700,
6849
+ letterSpacing: "0.08em",
6850
+ textTransform: "uppercase",
6851
+ lineHeight: 1.3,
6852
+ color: tone ? toneColor(theme, tone) : theme.palette.text.secondary,
6853
+ whiteSpace: "nowrap",
6854
+ overflow: "hidden",
6855
+ textOverflow: "ellipsis"
6856
+ }),
6857
+ children
6858
+ }
6859
+ );
6860
+ var InlineStat = ({ label, value, hint, tone = "accent", title }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { minWidth: 0, flex: 1 }, children: [
6861
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { display: "flex", alignItems: "center", gap: 0.625, minWidth: 0 }, children: [
6862
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6863
+ import_Box19.default,
6864
+ {
6865
+ sx: (theme) => ({
6866
+ width: 6,
6867
+ height: 6,
6868
+ borderRadius: "50%",
6869
+ bgcolor: toneColor(theme, tone),
6870
+ flexShrink: 0
6871
+ })
6872
+ }
6873
+ ),
6874
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tag, { children: label })
6875
+ ] }),
6876
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6877
+ import_Typography17.default,
6878
+ {
6879
+ title,
6880
+ sx: {
6881
+ fontSize: 19,
6882
+ fontWeight: 700,
6883
+ lineHeight: 1.15,
6884
+ mt: 0.25,
6885
+ fontVariantNumeric: "tabular-nums",
6886
+ whiteSpace: "nowrap",
6887
+ overflow: "hidden",
6888
+ textOverflow: "ellipsis"
6889
+ },
6890
+ children: value
6891
+ }
6892
+ ),
6893
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6894
+ import_Typography17.default,
6895
+ {
6896
+ variant: "caption",
6897
+ color: "text.secondary",
6898
+ sx: {
6899
+ display: "block",
6900
+ mt: 0.25,
6901
+ fontSize: 10.5,
6902
+ minHeight: 14,
6903
+ whiteSpace: "nowrap",
6904
+ overflow: "hidden",
6905
+ textOverflow: "ellipsis"
6906
+ },
6907
+ children: hint ?? ""
6908
+ }
6909
+ )
6910
+ ] });
6911
+ var LevelRow = ({
6819
6912
  gauge,
6820
6913
  size,
6821
6914
  keysHref
@@ -6824,14 +6917,42 @@ var LevelGauge = ({
6824
6917
  const limit = gauge.limit;
6825
6918
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
6826
6919
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
6920
+ const overCap = Boolean(limit && limit.pct > 100);
6921
+ const subNote = () => {
6922
+ if (overCap) return "Over cap";
6923
+ if (extra > 0 && keysHref) {
6924
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6925
+ import_Typography17.default,
6926
+ {
6927
+ component: import_core_components5.Link,
6928
+ to: keysHref,
6929
+ variant: "caption",
6930
+ sx: {
6931
+ fontSize: 10.5,
6932
+ color: "primary.main",
6933
+ textDecoration: "none",
6934
+ "&:hover": { textDecoration: "underline" }
6935
+ },
6936
+ children: [
6937
+ "+",
6938
+ extra,
6939
+ " more"
6940
+ ]
6941
+ }
6942
+ );
6943
+ }
6944
+ return limit?.sublabel ?? null;
6945
+ };
6827
6946
  const gaugeAriaLabel = limit ? `${name} budget used: ${Math.round(limit.pct)}%` : `${name} \u2014 no cap`;
6828
6947
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
6829
6948
  import_Box19.default,
6830
6949
  {
6831
6950
  sx: {
6832
- display: "flex",
6833
- flexDirection: "column",
6951
+ display: "grid",
6952
+ gridTemplateColumns: "auto minmax(0, 1fr) auto",
6834
6953
  alignItems: "center",
6954
+ gap: 1.25,
6955
+ py: 0.75,
6835
6956
  minWidth: 0
6836
6957
  },
6837
6958
  children: [
@@ -6845,84 +6966,109 @@ var LevelGauge = ({
6845
6966
  ariaLabel: gaugeAriaLabel
6846
6967
  }
6847
6968
  ),
6848
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6849
- import_Typography17.default,
6850
- {
6851
- sx: (theme) => ({
6852
- mt: 0.75,
6853
- fontSize: 10.5,
6854
- fontWeight: 700,
6855
- letterSpacing: "0.08em",
6856
- textTransform: "uppercase",
6857
- color: tone === "neutral" ? theme.palette.text.secondary : void 0
6858
- }),
6859
- children: name
6860
- }
6861
- ),
6862
- limit ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6863
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6969
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { minWidth: 0 }, children: [
6970
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tag, { tone: tone === "warning" || tone === "danger" ? tone : void 0, children: name }),
6971
+ limit ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6972
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6973
+ import_Typography17.default,
6974
+ {
6975
+ variant: "caption",
6976
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
6977
+ sx: {
6978
+ display: "block",
6979
+ fontSize: 11.5,
6980
+ fontWeight: 600,
6981
+ lineHeight: 1.35,
6982
+ overflow: "hidden",
6983
+ textOverflow: "ellipsis",
6984
+ whiteSpace: "nowrap"
6985
+ },
6986
+ children: limit.label
6987
+ }
6988
+ ),
6989
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6990
+ import_Typography17.default,
6991
+ {
6992
+ variant: "caption",
6993
+ color: overCap ? "error" : "text.secondary",
6994
+ sx: {
6995
+ display: "block",
6996
+ fontSize: 10.5,
6997
+ fontWeight: overCap ? 700 : void 0,
6998
+ lineHeight: 1.35,
6999
+ overflow: "hidden",
7000
+ textOverflow: "ellipsis",
7001
+ whiteSpace: "nowrap"
7002
+ },
7003
+ children: subNote()
7004
+ }
7005
+ )
7006
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6864
7007
  import_Typography17.default,
6865
7008
  {
6866
7009
  variant: "caption",
6867
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
7010
+ color: "text.secondary",
6868
7011
  sx: {
6869
- mt: 0.25,
6870
- fontSize: 11,
6871
- fontWeight: 600,
6872
- maxWidth: "100%",
7012
+ display: "block",
7013
+ fontSize: 10.5,
7014
+ lineHeight: 1.35,
6873
7015
  overflow: "hidden",
6874
7016
  textOverflow: "ellipsis",
6875
7017
  whiteSpace: "nowrap"
6876
7018
  },
6877
- children: limit.label
7019
+ children: none
6878
7020
  }
6879
- ),
7021
+ )
7022
+ ] }),
7023
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { minWidth: 0, textAlign: "right" }, children: [
6880
7024
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6881
7025
  import_Typography17.default,
6882
7026
  {
6883
- variant: "caption",
6884
- color: "text.secondary",
7027
+ title: limit ? `${fmtUsd(limit.spend)} of ${fmtUsd(limit.budget)}` : void 0,
6885
7028
  sx: {
6886
- fontSize: 10.5,
7029
+ display: "block",
7030
+ fontSize: 12.5,
7031
+ fontWeight: 600,
7032
+ lineHeight: 1.35,
6887
7033
  fontVariantNumeric: "tabular-nums",
6888
- textAlign: "center"
7034
+ whiteSpace: "nowrap",
7035
+ overflow: "hidden",
7036
+ textOverflow: "ellipsis",
7037
+ color: limit ? void 0 : "text.secondary"
6889
7038
  },
6890
- children: spendCaption(limit)
7039
+ children: amountCaption(limit)
6891
7040
  }
6892
7041
  ),
6893
- extra > 0 && keysHref ? /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7042
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6894
7043
  import_Typography17.default,
6895
7044
  {
6896
- component: import_core_components5.Link,
6897
- to: keysHref,
6898
7045
  variant: "caption",
7046
+ color: "text.secondary",
6899
7047
  sx: {
7048
+ display: "block",
6900
7049
  fontSize: 10.5,
6901
- color: "primary.main",
6902
- textDecoration: "none",
6903
- "&:hover": { textDecoration: "underline" }
7050
+ lineHeight: 1.35,
7051
+ whiteSpace: "nowrap",
7052
+ overflow: "hidden",
7053
+ textOverflow: "ellipsis"
6904
7054
  },
6905
- children: [
6906
- "+",
6907
- extra,
6908
- " more"
6909
- ]
7055
+ children: amountNote(limit)
6910
7056
  }
6911
- ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Typography17.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 }, children: resetLabel(limit) })
6912
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6913
- import_Typography17.default,
6914
- {
6915
- variant: "caption",
6916
- color: "text.secondary",
6917
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6918
- children: none
6919
- }
6920
- )
7057
+ )
7058
+ ] })
6921
7059
  ]
6922
7060
  }
6923
7061
  );
6924
7062
  };
6925
- var UsageMiniChart = ({ data, totalSpend, loading, height, usageUnavailable = false, maxBudget }) => {
7063
+ function amountCaption(limit) {
7064
+ if (!limit) return "";
7065
+ return limit.hidden ? `${Math.round(limit.pct)}%` : spendCaption(limit);
7066
+ }
7067
+ function amountNote(limit) {
7068
+ if (!limit) return "";
7069
+ return limit.hidden ? "hidden by admin" : resetLabel(limit);
7070
+ }
7071
+ var SpendStrip = ({ data, mtdSpend, loading, height, usageUnavailable = false, maxBudget }) => {
6926
7072
  const renderPlot = () => {
6927
7073
  if (loading) {
6928
7074
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "rectangular", width: "100%", height: "100%" });
@@ -6968,99 +7114,70 @@ var UsageMiniChart = ({ data, totalSpend, loading, height, usageUnavailable = fa
6968
7114
  )
6969
7115
  ] }) });
6970
7116
  };
6971
- const renderCaption = () => {
6972
- if (loading) {
6973
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Typography17.default, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 }, children: "\u2026" });
6974
- }
6975
- if (usageUnavailable) {
6976
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6977
- import_Typography17.default,
6978
- {
6979
- variant: "caption",
6980
- color: "text.secondary",
6981
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6982
- children: USAGE_UNAVAILABLE_MSG
6983
- }
6984
- );
6985
- }
6986
- if (data.length === 0) {
6987
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6988
- import_Typography17.default,
6989
- {
6990
- variant: "caption",
6991
- color: "text.secondary",
6992
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6993
- children: "No spend this month"
6994
- }
6995
- );
6996
- }
6997
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
6998
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
6999
- import_Typography17.default,
7000
- {
7001
- variant: "caption",
7002
- title: `${fmtUsd(totalSpend)} spent month to date`,
7003
- sx: {
7004
- mt: 0.25,
7005
- fontSize: 11,
7006
- fontWeight: 600,
7007
- maxWidth: "100%",
7008
- overflow: "hidden",
7009
- textOverflow: "ellipsis",
7010
- whiteSpace: "nowrap",
7011
- fontVariantNumeric: "tabular-nums"
7012
- },
7013
- children: fmtUsd(totalSpend)
7014
- }
7015
- ),
7016
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Typography17.default, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 }, children: "month to date" })
7017
- ] });
7018
- };
7019
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7020
- import_Box19.default,
7021
- {
7022
- sx: {
7023
- display: "flex",
7024
- flexDirection: "column",
7025
- alignItems: "center",
7026
- minWidth: 0
7027
- },
7028
- role: "img",
7029
- "aria-label": sparklineAriaLabel(data),
7030
- children: [
7031
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { height, width: "100%" }, children: renderPlot() }),
7032
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7033
- import_Typography17.default,
7034
- {
7035
- sx: (theme) => ({
7036
- mt: 0.75,
7037
- fontSize: 10.5,
7038
- fontWeight: 700,
7039
- letterSpacing: "0.08em",
7040
- textTransform: "uppercase",
7041
- color: theme.palette.text.secondary
7042
- }),
7043
- children: "Spend (MTD)"
7044
- }
7045
- ),
7046
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7047
- import_Typography17.default,
7048
- {
7049
- variant: "caption",
7050
- color: "text.secondary",
7051
- sx: { mt: 0.25, fontSize: 10.5 },
7052
- children: mtdCaption()
7053
- }
7054
- ),
7055
- renderCaption()
7056
- ]
7057
- }
7058
- );
7117
+ const caption = (() => {
7118
+ if (loading) return "\u2026";
7119
+ if (usageUnavailable) return USAGE_UNAVAILABLE_MSG;
7120
+ if (data.length === 0) return "No spend this month";
7121
+ return mtdCaption();
7122
+ })();
7123
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { role: "img", "aria-label": sparklineAriaLabel(data), sx: { minWidth: 0 }, children: [
7124
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { height, width: "100%" }, children: renderPlot() }),
7125
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7126
+ import_Box19.default,
7127
+ {
7128
+ sx: {
7129
+ display: "flex",
7130
+ alignItems: "baseline",
7131
+ justifyContent: "space-between",
7132
+ gap: 1,
7133
+ mt: 0.5,
7134
+ minWidth: 0
7135
+ },
7136
+ children: [
7137
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { display: "flex", alignItems: "baseline", gap: 0.75, minWidth: 0 }, children: [
7138
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Tag, { children: "Spent (mtd)" }),
7139
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7140
+ import_Typography17.default,
7141
+ {
7142
+ variant: "caption",
7143
+ color: "text.secondary",
7144
+ sx: { fontSize: 10.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
7145
+ children: caption
7146
+ }
7147
+ )
7148
+ ] }),
7149
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7150
+ import_Typography17.default,
7151
+ {
7152
+ title: loading || usageUnavailable ? void 0 : `${fmtUsd(mtdSpend)} spent month to date`,
7153
+ sx: {
7154
+ fontSize: 12.5,
7155
+ fontWeight: 700,
7156
+ fontVariantNumeric: "tabular-nums",
7157
+ whiteSpace: "nowrap"
7158
+ },
7159
+ children: loading || usageUnavailable ? "\u2014" : fmtUsd(mtdSpend)
7160
+ }
7161
+ )
7162
+ ]
7163
+ }
7164
+ )
7165
+ ] });
7059
7166
  };
7167
+ function statValue(loading, unavailable, value) {
7168
+ if (loading) return "\u2026";
7169
+ if (unavailable) return "\u2014";
7170
+ return value;
7171
+ }
7172
+ function statHint(loading, unavailable, hint) {
7173
+ if (loading) return void 0;
7174
+ if (unavailable) return USAGE_UNAVAILABLE_MSG;
7175
+ return hint;
7176
+ }
7060
7177
  var LiteLLMBudgetGauges = ({
7061
7178
  title = "Budget",
7062
7179
  bare = false,
7063
- size = 72,
7180
+ size = 56,
7064
7181
  keysHref,
7065
7182
  moduleHref: propModuleHref,
7066
7183
  onCreateKey,
@@ -7077,7 +7194,7 @@ var LiteLLMBudgetGauges = ({
7077
7194
  const api = (0, import_core_plugin_api7.useApi)(liteLlmApiRef);
7078
7195
  const theme = (0, import_styles13.useTheme)();
7079
7196
  const isSmallScreen = (0, import_useMediaQuery.default)(theme.breakpoints.down("sm"));
7080
- const gaugeSize = isSmallScreen ? 56 : size;
7197
+ const gaugeSize = isSmallScreen ? Math.min(48, size) : size;
7081
7198
  const expandedRegionId = (0, import_react15.useId)();
7082
7199
  const moduleRouteRef = (0, import_frontend_plugin_api6.useRouteRef)(rootRouteRef);
7083
7200
  const moduleHref = propModuleHref ?? moduleRouteRef?.();
@@ -7152,6 +7269,12 @@ var LiteLLMBudgetGauges = ({
7152
7269
  }),
7153
7270
  [loading, profileError, user, usageError, keys]
7154
7271
  );
7272
+ const today = /* @__PURE__ */ new Date();
7273
+ const todaySpend = spendOnDay(dailySpend, today);
7274
+ const yesterday = new Date(today);
7275
+ yesterday.setDate(yesterday.getDate() - 1);
7276
+ const yesterdaySpend = dailySpend.some((d) => d.date === toLocalDay(yesterday)) ? spendOnDay(dailySpend, yesterday) : null;
7277
+ const todayHint = yesterdaySpend === null ? void 0 : `vs ${fmtUsd(yesterdaySpend)} yesterday`;
7155
7278
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
7156
7279
  const resolvedCtas = (0, import_react15.useMemo)(() => {
7157
7280
  return resolveCtas({
@@ -7212,29 +7335,70 @@ var LiteLLMBudgetGauges = ({
7212
7335
  }
7213
7336
  )
7214
7337
  ] }),
7215
- viewState.kind === "loading" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { display: "flex", alignItems: "flex-start", gap: 1, minHeight: size }, children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { display: "flex", flexDirection: "column", alignItems: "center", flex: 1 }, children: [
7216
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "circular", width: size, height: size, sx: { mb: 0.75 } }),
7217
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: "100%", height: 12, sx: { mb: 0.25 } }),
7218
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: "80%", height: 11, sx: { mb: 0.25 } }),
7219
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: "70%", height: 10.5 })
7338
+ viewState.kind === "loading" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25, minHeight: size }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { display: "flex", alignItems: "center", gap: 1.25 }, children: [
7339
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "circular", width: size, height: size }),
7340
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { flex: 1 }, children: [
7341
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: "70%", height: 12, sx: { mb: 0.25 } }),
7342
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: "45%", height: 11 })
7343
+ ] }),
7344
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Skeleton8.default, { variant: "text", width: 64, height: 12 })
7220
7345
  ] }, i)) }),
7221
7346
  viewState.kind === "error" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Typography17.default, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: viewState.message }),
7222
7347
  viewState.kind === "unprovisioned" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Typography17.default, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: UNPROVISIONED_MSG }),
7223
7348
  viewState.kind === "ready" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
7224
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_Box19.default, { sx: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(76px, 1fr))", gap: 1 }, children: [
7225
- gauges.map((gauge) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LevelGauge, { gauge, size: gaugeSize, keysHref: keysTabHref }, gauge.kind)),
7226
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7227
- UsageMiniChart,
7228
- {
7229
- data: dailySpend,
7230
- totalSpend: mtdSpend,
7231
- loading: usageLoading,
7232
- height: gaugeSize,
7233
- usageUnavailable: viewState.usageUnavailable,
7234
- maxBudget: user?.max_budget ?? void 0
7235
- }
7236
- )
7237
- ] }),
7349
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
7350
+ import_Box19.default,
7351
+ {
7352
+ sx: {
7353
+ display: "flex",
7354
+ alignItems: "stretch",
7355
+ gap: 2,
7356
+ mb: 1.25,
7357
+ minWidth: 0
7358
+ },
7359
+ children: [
7360
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7361
+ InlineStat,
7362
+ {
7363
+ label: "Today spent",
7364
+ tone: "accent",
7365
+ value: statValue(usageLoading, viewState.usageUnavailable, fmtUsd(todaySpend)),
7366
+ hint: statHint(usageLoading, viewState.usageUnavailable, todayHint),
7367
+ title: usageLoading || viewState.usageUnavailable ? void 0 : "Spend since local midnight"
7368
+ }
7369
+ ),
7370
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { width: "1px", bgcolor: "divider", flexShrink: 0 } }),
7371
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7372
+ InlineStat,
7373
+ {
7374
+ label: "Month to date",
7375
+ value: statValue(usageLoading, viewState.usageUnavailable, fmtUsd(mtdSpend)),
7376
+ hint: statHint(usageLoading, viewState.usageUnavailable, mtdCaption()),
7377
+ title: usageLoading || viewState.usageUnavailable ? void 0 : "Spend from the 1st of this month"
7378
+ }
7379
+ )
7380
+ ]
7381
+ }
7382
+ ),
7383
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { borderTop: "1px solid", borderColor: "divider" }, children: gauges.map((gauge, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7384
+ import_Box19.default,
7385
+ {
7386
+ sx: i === 0 ? void 0 : { borderTop: "1px solid", borderColor: "divider" },
7387
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LevelRow, { gauge, size: gaugeSize, keysHref: keysTabHref })
7388
+ },
7389
+ gauge.kind
7390
+ )) }),
7391
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Box19.default, { sx: { mt: 1.25 }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7392
+ SpendStrip,
7393
+ {
7394
+ data: dailySpend,
7395
+ mtdSpend,
7396
+ loading: usageLoading,
7397
+ height: gaugeSize,
7398
+ usageUnavailable: viewState.usageUnavailable,
7399
+ maxBudget: user?.max_budget ?? void 0
7400
+ }
7401
+ ) }),
7238
7402
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_Collapse4.default, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
7239
7403
  import_Box19.default,
7240
7404
  {