@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.esm.js CHANGED
@@ -1038,16 +1038,21 @@ var init_Gauge = __esm({
1038
1038
  fontSize: size <= 64 ? 15 : 18,
1039
1039
  fontWeight: 700,
1040
1040
  fontVariantNumeric: "tabular-nums",
1041
- color: "text.primary"
1041
+ color: isOverCap ? "currentColor" : "text.primary"
1042
1042
  },
1043
- children: isOverCap ? "Over cap" : label ?? `${Math.round(value)}%`
1043
+ children: label ?? `${Math.round(value)}%`
1044
1044
  }
1045
1045
  ),
1046
1046
  caption && /* @__PURE__ */ jsx5(
1047
1047
  Typography3,
1048
1048
  {
1049
1049
  variant: "caption",
1050
- sx: { fontSize: 9.5, color: "text.secondary", mt: 0.25 },
1050
+ sx: {
1051
+ fontSize: 9.5,
1052
+ color: "text.secondary",
1053
+ mt: 0.25,
1054
+ whiteSpace: "nowrap"
1055
+ },
1051
1056
  children: caption
1052
1057
  }
1053
1058
  )
@@ -1500,6 +1505,18 @@ function pruneCopy(n) {
1500
1505
  }
1501
1506
  return `Remove ${n} expired keys from the list. They already don't work.`;
1502
1507
  }
1508
+ function blockButtonState(blocked, manage, unblock) {
1509
+ if (blocked) {
1510
+ return {
1511
+ label: unblock.denied ? "No permission to unblock keys" : "Unblock key",
1512
+ disabled: !unblock.allowed
1513
+ };
1514
+ }
1515
+ return {
1516
+ label: manage.denied ? "No permission to block keys" : "Block key \u2014 suspends without revoking",
1517
+ disabled: !manage.allowed
1518
+ };
1519
+ }
1503
1520
  var init_keyLabels = __esm({
1504
1521
  "src/keyLabels.ts"() {
1505
1522
  "use strict";
@@ -1603,7 +1620,8 @@ import { ContentCopy, Delete, Edit, Autorenew, Search, Lock, LockOpen, Check, Cl
1603
1620
  import { usePermission } from "@backstage/plugin-permission-react";
1604
1621
  import {
1605
1622
  litellmKeyManagePermission,
1606
- litellmKeyRevokePermission
1623
+ litellmKeyRevokePermission,
1624
+ litellmKeyUnblockPermission
1607
1625
  } from "@acarmisc/backstage-plugin-litellm-common";
1608
1626
  import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1609
1627
  function expiryChipLabel(status, expiresAt) {
@@ -1717,10 +1735,13 @@ var init_KeysTable = __esm({
1717
1735
  }) => {
1718
1736
  const managePermission = usePermission({ permission: litellmKeyManagePermission });
1719
1737
  const revokePermission = usePermission({ permission: litellmKeyRevokePermission });
1738
+ const unblockPermission = usePermission({ permission: litellmKeyUnblockPermission });
1720
1739
  const canManageKeys = !managePermission.loading && managePermission.allowed;
1721
1740
  const canRevokeKeys = !revokePermission.loading && revokePermission.allowed;
1722
1741
  const manageDenied = !managePermission.loading && !managePermission.allowed;
1723
1742
  const revokeDenied = !revokePermission.loading && !revokePermission.allowed;
1743
+ const canUnblockKeys = !unblockPermission.loading && unblockPermission.allowed;
1744
+ const unblockDenied = !unblockPermission.loading && !unblockPermission.allowed;
1724
1745
  const [blockConfirmKey, setBlockConfirmKey] = useState3(null);
1725
1746
  const [blockSubmitting, setBlockSubmitting] = useState3(false);
1726
1747
  const [deleteConfirmKey, setDeleteConfirmKey] = useState3(null);
@@ -1839,6 +1860,11 @@ var init_KeysTable = __esm({
1839
1860
  return filteredKeys.map((key) => {
1840
1861
  const keyId = key.token ?? key.key;
1841
1862
  const keyModels = key.models ?? [];
1863
+ const blockState = blockButtonState(
1864
+ key.blocked,
1865
+ { allowed: canManageKeys, denied: manageDenied },
1866
+ { allowed: canUnblockKeys, denied: unblockDenied }
1867
+ );
1842
1868
  return /* @__PURE__ */ jsxs7(
1843
1869
  TableRow,
1844
1870
  {
@@ -1910,19 +1936,13 @@ var init_KeysTable = __esm({
1910
1936
  children: /* @__PURE__ */ jsx8(Edit, { fontSize: "small" })
1911
1937
  }
1912
1938
  ) }) }),
1913
- /* @__PURE__ */ jsx8(Tooltip, { describeChild: true, title: (() => {
1914
- if (manageDenied) return "No permission to block keys";
1915
- return key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking";
1916
- })(), placement: "top", children: /* @__PURE__ */ jsx8("span", { children: /* @__PURE__ */ jsx8(
1939
+ /* @__PURE__ */ jsx8(Tooltip, { describeChild: true, title: blockState.label, placement: "top", children: /* @__PURE__ */ jsx8("span", { children: /* @__PURE__ */ jsx8(
1917
1940
  IconButton,
1918
1941
  {
1919
1942
  size: "small",
1920
1943
  onClick: () => handleToggleBlock(key),
1921
- disabled: blockSubmitting || !canManageKeys,
1922
- "aria-label": (() => {
1923
- if (manageDenied) return "No permission to block keys";
1924
- return key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking";
1925
- })(),
1944
+ disabled: blockSubmitting || blockState.disabled,
1945
+ "aria-label": blockState.label,
1926
1946
  sx: quietIconButtonSx("warning"),
1927
1947
  children: keyBlockIcon(blockSubmitting, key.blocked)
1928
1948
  }
@@ -5750,7 +5770,7 @@ import {
5750
5770
  litellmKeyRevokePermission as litellmKeyRevokePermission2,
5751
5771
  litellmKeyManagePermission as litellmKeyManagePermission2,
5752
5772
  litellmKeyResetSpendPermission as litellmKeyResetSpendPermission2,
5753
- litellmKeyUnblockPermission,
5773
+ litellmKeyUnblockPermission as litellmKeyUnblockPermission2,
5754
5774
  litellmAuditReadPermission,
5755
5775
  litellmTeamCreatePermission,
5756
5776
  litellmTeamManagePermission,
@@ -6558,6 +6578,7 @@ import { Link as Link3 } from "@backstage/core-components";
6558
6578
  // src/homeWidgetHelpers.ts
6559
6579
  init_format();
6560
6580
  init_tokens();
6581
+ init_dates();
6561
6582
  function rangeCaption(startDay, endDay) {
6562
6583
  return `${fmtDateShort(startDay)} \u2013 ${fmtDateShort(endDay)}`;
6563
6584
  }
@@ -6582,6 +6603,10 @@ function sparklineAriaLabel(points) {
6582
6603
  const peakDate = fmtDateShort(maxPoint.date);
6583
6604
  return `Daily spend over ${dayCount} day${dayCount === 1 ? "" : "s"}, total ${fmtUsd(total)}, peak ${fmtUsd(maxPoint.spend)} on ${peakDate}`;
6584
6605
  }
6606
+ function spendOnDay(points, now = /* @__PURE__ */ new Date()) {
6607
+ const day = toLocalDay(now);
6608
+ return points.find((p) => p.date === day)?.spend ?? 0;
6609
+ }
6585
6610
  function usageSummary(usage) {
6586
6611
  if (!usage) {
6587
6612
  return "$0.00 spent \xB7 0 in \xB7 0 out";
@@ -6810,7 +6835,76 @@ function resetLabel(limit) {
6810
6835
  const window2 = fmtBudgetDuration(limit.budgetDuration);
6811
6836
  return window2 ? `resets ${window2}` : "never resets";
6812
6837
  }
6813
- var LevelGauge = ({
6838
+ var Tag = ({ children, tone, title }) => /* @__PURE__ */ jsx21(
6839
+ Typography17,
6840
+ {
6841
+ title,
6842
+ sx: (theme) => ({
6843
+ fontSize: 10.5,
6844
+ fontWeight: 700,
6845
+ letterSpacing: "0.08em",
6846
+ textTransform: "uppercase",
6847
+ lineHeight: 1.3,
6848
+ color: tone ? toneColor(theme, tone) : theme.palette.text.secondary,
6849
+ whiteSpace: "nowrap",
6850
+ overflow: "hidden",
6851
+ textOverflow: "ellipsis"
6852
+ }),
6853
+ children
6854
+ }
6855
+ );
6856
+ var InlineStat = ({ label, value, hint, tone = "accent", title }) => /* @__PURE__ */ jsxs19(Box19, { sx: { minWidth: 0, flex: 1 }, children: [
6857
+ /* @__PURE__ */ jsxs19(Box19, { sx: { display: "flex", alignItems: "center", gap: 0.625, minWidth: 0 }, children: [
6858
+ /* @__PURE__ */ jsx21(
6859
+ Box19,
6860
+ {
6861
+ sx: (theme) => ({
6862
+ width: 6,
6863
+ height: 6,
6864
+ borderRadius: "50%",
6865
+ bgcolor: toneColor(theme, tone),
6866
+ flexShrink: 0
6867
+ })
6868
+ }
6869
+ ),
6870
+ /* @__PURE__ */ jsx21(Tag, { children: label })
6871
+ ] }),
6872
+ /* @__PURE__ */ jsx21(
6873
+ Typography17,
6874
+ {
6875
+ title,
6876
+ sx: {
6877
+ fontSize: 19,
6878
+ fontWeight: 700,
6879
+ lineHeight: 1.15,
6880
+ mt: 0.25,
6881
+ fontVariantNumeric: "tabular-nums",
6882
+ whiteSpace: "nowrap",
6883
+ overflow: "hidden",
6884
+ textOverflow: "ellipsis"
6885
+ },
6886
+ children: value
6887
+ }
6888
+ ),
6889
+ /* @__PURE__ */ jsx21(
6890
+ Typography17,
6891
+ {
6892
+ variant: "caption",
6893
+ color: "text.secondary",
6894
+ sx: {
6895
+ display: "block",
6896
+ mt: 0.25,
6897
+ fontSize: 10.5,
6898
+ minHeight: 14,
6899
+ whiteSpace: "nowrap",
6900
+ overflow: "hidden",
6901
+ textOverflow: "ellipsis"
6902
+ },
6903
+ children: hint ?? ""
6904
+ }
6905
+ )
6906
+ ] });
6907
+ var LevelRow = ({
6814
6908
  gauge,
6815
6909
  size,
6816
6910
  keysHref
@@ -6819,14 +6913,42 @@ var LevelGauge = ({
6819
6913
  const limit = gauge.limit;
6820
6914
  const tone = limit ? budgetTone2(limit.pct) : "neutral";
6821
6915
  const extra = gauge.kind === "key" ? Math.max(0, gauge.count - 1) : 0;
6916
+ const overCap = Boolean(limit && limit.pct > 100);
6917
+ const subNote = () => {
6918
+ if (overCap) return "Over cap";
6919
+ if (extra > 0 && keysHref) {
6920
+ return /* @__PURE__ */ jsxs19(
6921
+ Typography17,
6922
+ {
6923
+ component: Link4,
6924
+ to: keysHref,
6925
+ variant: "caption",
6926
+ sx: {
6927
+ fontSize: 10.5,
6928
+ color: "primary.main",
6929
+ textDecoration: "none",
6930
+ "&:hover": { textDecoration: "underline" }
6931
+ },
6932
+ children: [
6933
+ "+",
6934
+ extra,
6935
+ " more"
6936
+ ]
6937
+ }
6938
+ );
6939
+ }
6940
+ return limit?.sublabel ?? null;
6941
+ };
6822
6942
  const gaugeAriaLabel = limit ? `${name} budget used: ${Math.round(limit.pct)}%` : `${name} \u2014 no cap`;
6823
6943
  return /* @__PURE__ */ jsxs19(
6824
6944
  Box19,
6825
6945
  {
6826
6946
  sx: {
6827
- display: "flex",
6828
- flexDirection: "column",
6947
+ display: "grid",
6948
+ gridTemplateColumns: "auto minmax(0, 1fr) auto",
6829
6949
  alignItems: "center",
6950
+ gap: 1.25,
6951
+ py: 0.75,
6830
6952
  minWidth: 0
6831
6953
  },
6832
6954
  children: [
@@ -6840,84 +6962,109 @@ var LevelGauge = ({
6840
6962
  ariaLabel: gaugeAriaLabel
6841
6963
  }
6842
6964
  ),
6843
- /* @__PURE__ */ jsx21(
6844
- Typography17,
6845
- {
6846
- sx: (theme) => ({
6847
- mt: 0.75,
6848
- fontSize: 10.5,
6849
- fontWeight: 700,
6850
- letterSpacing: "0.08em",
6851
- textTransform: "uppercase",
6852
- color: tone === "neutral" ? theme.palette.text.secondary : void 0
6853
- }),
6854
- children: name
6855
- }
6856
- ),
6857
- limit ? /* @__PURE__ */ jsxs19(Fragment11, { children: [
6858
- /* @__PURE__ */ jsx21(
6965
+ /* @__PURE__ */ jsxs19(Box19, { sx: { minWidth: 0 }, children: [
6966
+ /* @__PURE__ */ jsx21(Tag, { tone: tone === "warning" || tone === "danger" ? tone : void 0, children: name }),
6967
+ limit ? /* @__PURE__ */ jsxs19(Fragment11, { children: [
6968
+ /* @__PURE__ */ jsx21(
6969
+ Typography17,
6970
+ {
6971
+ variant: "caption",
6972
+ title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
6973
+ sx: {
6974
+ display: "block",
6975
+ fontSize: 11.5,
6976
+ fontWeight: 600,
6977
+ lineHeight: 1.35,
6978
+ overflow: "hidden",
6979
+ textOverflow: "ellipsis",
6980
+ whiteSpace: "nowrap"
6981
+ },
6982
+ children: limit.label
6983
+ }
6984
+ ),
6985
+ /* @__PURE__ */ jsx21(
6986
+ Typography17,
6987
+ {
6988
+ variant: "caption",
6989
+ color: overCap ? "error" : "text.secondary",
6990
+ sx: {
6991
+ display: "block",
6992
+ fontSize: 10.5,
6993
+ fontWeight: overCap ? 700 : void 0,
6994
+ lineHeight: 1.35,
6995
+ overflow: "hidden",
6996
+ textOverflow: "ellipsis",
6997
+ whiteSpace: "nowrap"
6998
+ },
6999
+ children: subNote()
7000
+ }
7001
+ )
7002
+ ] }) : /* @__PURE__ */ jsx21(
6859
7003
  Typography17,
6860
7004
  {
6861
7005
  variant: "caption",
6862
- title: limit.sublabel ? `${limit.label} \xB7 ${limit.sublabel}` : limit.label,
7006
+ color: "text.secondary",
6863
7007
  sx: {
6864
- mt: 0.25,
6865
- fontSize: 11,
6866
- fontWeight: 600,
6867
- maxWidth: "100%",
7008
+ display: "block",
7009
+ fontSize: 10.5,
7010
+ lineHeight: 1.35,
6868
7011
  overflow: "hidden",
6869
7012
  textOverflow: "ellipsis",
6870
7013
  whiteSpace: "nowrap"
6871
7014
  },
6872
- children: limit.label
7015
+ children: none
6873
7016
  }
6874
- ),
7017
+ )
7018
+ ] }),
7019
+ /* @__PURE__ */ jsxs19(Box19, { sx: { minWidth: 0, textAlign: "right" }, children: [
6875
7020
  /* @__PURE__ */ jsx21(
6876
7021
  Typography17,
6877
7022
  {
6878
- variant: "caption",
6879
- color: "text.secondary",
7023
+ title: limit ? `${fmtUsd(limit.spend)} of ${fmtUsd(limit.budget)}` : void 0,
6880
7024
  sx: {
6881
- fontSize: 10.5,
7025
+ display: "block",
7026
+ fontSize: 12.5,
7027
+ fontWeight: 600,
7028
+ lineHeight: 1.35,
6882
7029
  fontVariantNumeric: "tabular-nums",
6883
- textAlign: "center"
7030
+ whiteSpace: "nowrap",
7031
+ overflow: "hidden",
7032
+ textOverflow: "ellipsis",
7033
+ color: limit ? void 0 : "text.secondary"
6884
7034
  },
6885
- children: spendCaption(limit)
7035
+ children: amountCaption(limit)
6886
7036
  }
6887
7037
  ),
6888
- extra > 0 && keysHref ? /* @__PURE__ */ jsxs19(
7038
+ /* @__PURE__ */ jsx21(
6889
7039
  Typography17,
6890
7040
  {
6891
- component: Link4,
6892
- to: keysHref,
6893
7041
  variant: "caption",
7042
+ color: "text.secondary",
6894
7043
  sx: {
7044
+ display: "block",
6895
7045
  fontSize: 10.5,
6896
- color: "primary.main",
6897
- textDecoration: "none",
6898
- "&:hover": { textDecoration: "underline" }
7046
+ lineHeight: 1.35,
7047
+ whiteSpace: "nowrap",
7048
+ overflow: "hidden",
7049
+ textOverflow: "ellipsis"
6899
7050
  },
6900
- children: [
6901
- "+",
6902
- extra,
6903
- " more"
6904
- ]
7051
+ children: amountNote(limit)
6905
7052
  }
6906
- ) : /* @__PURE__ */ jsx21(Typography17, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 }, children: resetLabel(limit) })
6907
- ] }) : /* @__PURE__ */ jsx21(
6908
- Typography17,
6909
- {
6910
- variant: "caption",
6911
- color: "text.secondary",
6912
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6913
- children: none
6914
- }
6915
- )
7053
+ )
7054
+ ] })
6916
7055
  ]
6917
7056
  }
6918
7057
  );
6919
7058
  };
6920
- var UsageMiniChart = ({ data, totalSpend, loading, height, usageUnavailable = false, maxBudget }) => {
7059
+ function amountCaption(limit) {
7060
+ if (!limit) return "";
7061
+ return limit.hidden ? `${Math.round(limit.pct)}%` : spendCaption(limit);
7062
+ }
7063
+ function amountNote(limit) {
7064
+ if (!limit) return "";
7065
+ return limit.hidden ? "hidden by admin" : resetLabel(limit);
7066
+ }
7067
+ var SpendStrip = ({ data, mtdSpend, loading, height, usageUnavailable = false, maxBudget }) => {
6921
7068
  const renderPlot = () => {
6922
7069
  if (loading) {
6923
7070
  return /* @__PURE__ */ jsx21(Skeleton8, { variant: "rectangular", width: "100%", height: "100%" });
@@ -6963,99 +7110,70 @@ var UsageMiniChart = ({ data, totalSpend, loading, height, usageUnavailable = fa
6963
7110
  )
6964
7111
  ] }) });
6965
7112
  };
6966
- const renderCaption = () => {
6967
- if (loading) {
6968
- return /* @__PURE__ */ jsx21(Typography17, { variant: "caption", color: "text.secondary", sx: { mt: 0.25, fontSize: 11 }, children: "\u2026" });
6969
- }
6970
- if (usageUnavailable) {
6971
- return /* @__PURE__ */ jsx21(
6972
- Typography17,
6973
- {
6974
- variant: "caption",
6975
- color: "text.secondary",
6976
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6977
- children: USAGE_UNAVAILABLE_MSG
6978
- }
6979
- );
6980
- }
6981
- if (data.length === 0) {
6982
- return /* @__PURE__ */ jsx21(
6983
- Typography17,
6984
- {
6985
- variant: "caption",
6986
- color: "text.secondary",
6987
- sx: { mt: 0.25, fontSize: 10.5, textAlign: "center" },
6988
- children: "No spend this month"
6989
- }
6990
- );
6991
- }
6992
- return /* @__PURE__ */ jsxs19(Fragment11, { children: [
6993
- /* @__PURE__ */ jsx21(
6994
- Typography17,
6995
- {
6996
- variant: "caption",
6997
- title: `${fmtUsd(totalSpend)} spent month to date`,
6998
- sx: {
6999
- mt: 0.25,
7000
- fontSize: 11,
7001
- fontWeight: 600,
7002
- maxWidth: "100%",
7003
- overflow: "hidden",
7004
- textOverflow: "ellipsis",
7005
- whiteSpace: "nowrap",
7006
- fontVariantNumeric: "tabular-nums"
7007
- },
7008
- children: fmtUsd(totalSpend)
7009
- }
7010
- ),
7011
- /* @__PURE__ */ jsx21(Typography17, { variant: "caption", color: "text.secondary", sx: { fontSize: 10.5 }, children: "month to date" })
7012
- ] });
7013
- };
7014
- return /* @__PURE__ */ jsxs19(
7015
- Box19,
7016
- {
7017
- sx: {
7018
- display: "flex",
7019
- flexDirection: "column",
7020
- alignItems: "center",
7021
- minWidth: 0
7022
- },
7023
- role: "img",
7024
- "aria-label": sparklineAriaLabel(data),
7025
- children: [
7026
- /* @__PURE__ */ jsx21(Box19, { sx: { height, width: "100%" }, children: renderPlot() }),
7027
- /* @__PURE__ */ jsx21(
7028
- Typography17,
7029
- {
7030
- sx: (theme) => ({
7031
- mt: 0.75,
7032
- fontSize: 10.5,
7033
- fontWeight: 700,
7034
- letterSpacing: "0.08em",
7035
- textTransform: "uppercase",
7036
- color: theme.palette.text.secondary
7037
- }),
7038
- children: "Spend (MTD)"
7039
- }
7040
- ),
7041
- /* @__PURE__ */ jsx21(
7042
- Typography17,
7043
- {
7044
- variant: "caption",
7045
- color: "text.secondary",
7046
- sx: { mt: 0.25, fontSize: 10.5 },
7047
- children: mtdCaption()
7048
- }
7049
- ),
7050
- renderCaption()
7051
- ]
7052
- }
7053
- );
7113
+ const caption = (() => {
7114
+ if (loading) return "\u2026";
7115
+ if (usageUnavailable) return USAGE_UNAVAILABLE_MSG;
7116
+ if (data.length === 0) return "No spend this month";
7117
+ return mtdCaption();
7118
+ })();
7119
+ return /* @__PURE__ */ jsxs19(Box19, { role: "img", "aria-label": sparklineAriaLabel(data), sx: { minWidth: 0 }, children: [
7120
+ /* @__PURE__ */ jsx21(Box19, { sx: { height, width: "100%" }, children: renderPlot() }),
7121
+ /* @__PURE__ */ jsxs19(
7122
+ Box19,
7123
+ {
7124
+ sx: {
7125
+ display: "flex",
7126
+ alignItems: "baseline",
7127
+ justifyContent: "space-between",
7128
+ gap: 1,
7129
+ mt: 0.5,
7130
+ minWidth: 0
7131
+ },
7132
+ children: [
7133
+ /* @__PURE__ */ jsxs19(Box19, { sx: { display: "flex", alignItems: "baseline", gap: 0.75, minWidth: 0 }, children: [
7134
+ /* @__PURE__ */ jsx21(Tag, { children: "Spent (mtd)" }),
7135
+ /* @__PURE__ */ jsx21(
7136
+ Typography17,
7137
+ {
7138
+ variant: "caption",
7139
+ color: "text.secondary",
7140
+ sx: { fontSize: 10.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
7141
+ children: caption
7142
+ }
7143
+ )
7144
+ ] }),
7145
+ /* @__PURE__ */ jsx21(
7146
+ Typography17,
7147
+ {
7148
+ title: loading || usageUnavailable ? void 0 : `${fmtUsd(mtdSpend)} spent month to date`,
7149
+ sx: {
7150
+ fontSize: 12.5,
7151
+ fontWeight: 700,
7152
+ fontVariantNumeric: "tabular-nums",
7153
+ whiteSpace: "nowrap"
7154
+ },
7155
+ children: loading || usageUnavailable ? "\u2014" : fmtUsd(mtdSpend)
7156
+ }
7157
+ )
7158
+ ]
7159
+ }
7160
+ )
7161
+ ] });
7054
7162
  };
7163
+ function statValue(loading, unavailable, value) {
7164
+ if (loading) return "\u2026";
7165
+ if (unavailable) return "\u2014";
7166
+ return value;
7167
+ }
7168
+ function statHint(loading, unavailable, hint) {
7169
+ if (loading) return void 0;
7170
+ if (unavailable) return USAGE_UNAVAILABLE_MSG;
7171
+ return hint;
7172
+ }
7055
7173
  var LiteLLMBudgetGauges = ({
7056
7174
  title = "Budget",
7057
7175
  bare = false,
7058
- size = 72,
7176
+ size = 56,
7059
7177
  keysHref,
7060
7178
  moduleHref: propModuleHref,
7061
7179
  onCreateKey,
@@ -7072,7 +7190,7 @@ var LiteLLMBudgetGauges = ({
7072
7190
  const api = useApi6(liteLlmApiRef);
7073
7191
  const theme = useTheme4();
7074
7192
  const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
7075
- const gaugeSize = isSmallScreen ? 56 : size;
7193
+ const gaugeSize = isSmallScreen ? Math.min(48, size) : size;
7076
7194
  const expandedRegionId = useId2();
7077
7195
  const moduleRouteRef = useRouteRef3(rootRouteRef);
7078
7196
  const moduleHref = propModuleHref ?? moduleRouteRef?.();
@@ -7147,6 +7265,12 @@ var LiteLLMBudgetGauges = ({
7147
7265
  }),
7148
7266
  [loading, profileError, user, usageError, keys]
7149
7267
  );
7268
+ const today = /* @__PURE__ */ new Date();
7269
+ const todaySpend = spendOnDay(dailySpend, today);
7270
+ const yesterday = new Date(today);
7271
+ yesterday.setDate(yesterday.getDate() - 1);
7272
+ const yesterdaySpend = dailySpend.some((d) => d.date === toLocalDay(yesterday)) ? spendOnDay(dailySpend, yesterday) : null;
7273
+ const todayHint = yesterdaySpend === null ? void 0 : `vs ${fmtUsd(yesterdaySpend)} yesterday`;
7150
7274
  const summaryText = headline.count === 0 ? "no limits apply" : `${headline.count} limit${headline.count === 1 ? "" : "s"}`;
7151
7275
  const resolvedCtas = useMemo11(() => {
7152
7276
  return resolveCtas({
@@ -7207,29 +7331,70 @@ var LiteLLMBudgetGauges = ({
7207
7331
  }
7208
7332
  )
7209
7333
  ] }),
7210
- viewState.kind === "loading" && /* @__PURE__ */ jsx21(Box19, { sx: { display: "flex", alignItems: "flex-start", gap: 1, minHeight: size }, children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs19(Box19, { sx: { display: "flex", flexDirection: "column", alignItems: "center", flex: 1 }, children: [
7211
- /* @__PURE__ */ jsx21(Skeleton8, { variant: "circular", width: size, height: size, sx: { mb: 0.75 } }),
7212
- /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: "100%", height: 12, sx: { mb: 0.25 } }),
7213
- /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: "80%", height: 11, sx: { mb: 0.25 } }),
7214
- /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: "70%", height: 10.5 })
7334
+ viewState.kind === "loading" && /* @__PURE__ */ jsx21(Box19, { sx: { display: "flex", flexDirection: "column", gap: 1.25, minHeight: size }, children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs19(Box19, { sx: { display: "flex", alignItems: "center", gap: 1.25 }, children: [
7335
+ /* @__PURE__ */ jsx21(Skeleton8, { variant: "circular", width: size, height: size }),
7336
+ /* @__PURE__ */ jsxs19(Box19, { sx: { flex: 1 }, children: [
7337
+ /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: "70%", height: 12, sx: { mb: 0.25 } }),
7338
+ /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: "45%", height: 11 })
7339
+ ] }),
7340
+ /* @__PURE__ */ jsx21(Skeleton8, { variant: "text", width: 64, height: 12 })
7215
7341
  ] }, i)) }),
7216
7342
  viewState.kind === "error" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: viewState.message }),
7217
7343
  viewState.kind === "unprovisioned" && /* @__PURE__ */ jsx21(Typography17, { variant: "body2", color: "text.secondary", sx: { mt: 0.5 }, children: UNPROVISIONED_MSG }),
7218
7344
  viewState.kind === "ready" && /* @__PURE__ */ jsxs19(Fragment11, { children: [
7219
- /* @__PURE__ */ jsxs19(Box19, { sx: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(76px, 1fr))", gap: 1 }, children: [
7220
- gauges.map((gauge) => /* @__PURE__ */ jsx21(LevelGauge, { gauge, size: gaugeSize, keysHref: keysTabHref }, gauge.kind)),
7221
- /* @__PURE__ */ jsx21(
7222
- UsageMiniChart,
7223
- {
7224
- data: dailySpend,
7225
- totalSpend: mtdSpend,
7226
- loading: usageLoading,
7227
- height: gaugeSize,
7228
- usageUnavailable: viewState.usageUnavailable,
7229
- maxBudget: user?.max_budget ?? void 0
7230
- }
7231
- )
7232
- ] }),
7345
+ /* @__PURE__ */ jsxs19(
7346
+ Box19,
7347
+ {
7348
+ sx: {
7349
+ display: "flex",
7350
+ alignItems: "stretch",
7351
+ gap: 2,
7352
+ mb: 1.25,
7353
+ minWidth: 0
7354
+ },
7355
+ children: [
7356
+ /* @__PURE__ */ jsx21(
7357
+ InlineStat,
7358
+ {
7359
+ label: "Today spent",
7360
+ tone: "accent",
7361
+ value: statValue(usageLoading, viewState.usageUnavailable, fmtUsd(todaySpend)),
7362
+ hint: statHint(usageLoading, viewState.usageUnavailable, todayHint),
7363
+ title: usageLoading || viewState.usageUnavailable ? void 0 : "Spend since local midnight"
7364
+ }
7365
+ ),
7366
+ /* @__PURE__ */ jsx21(Box19, { sx: { width: "1px", bgcolor: "divider", flexShrink: 0 } }),
7367
+ /* @__PURE__ */ jsx21(
7368
+ InlineStat,
7369
+ {
7370
+ label: "Month to date",
7371
+ value: statValue(usageLoading, viewState.usageUnavailable, fmtUsd(mtdSpend)),
7372
+ hint: statHint(usageLoading, viewState.usageUnavailable, mtdCaption()),
7373
+ title: usageLoading || viewState.usageUnavailable ? void 0 : "Spend from the 1st of this month"
7374
+ }
7375
+ )
7376
+ ]
7377
+ }
7378
+ ),
7379
+ /* @__PURE__ */ jsx21(Box19, { sx: { borderTop: "1px solid", borderColor: "divider" }, children: gauges.map((gauge, i) => /* @__PURE__ */ jsx21(
7380
+ Box19,
7381
+ {
7382
+ sx: i === 0 ? void 0 : { borderTop: "1px solid", borderColor: "divider" },
7383
+ children: /* @__PURE__ */ jsx21(LevelRow, { gauge, size: gaugeSize, keysHref: keysTabHref })
7384
+ },
7385
+ gauge.kind
7386
+ )) }),
7387
+ /* @__PURE__ */ jsx21(Box19, { sx: { mt: 1.25 }, children: /* @__PURE__ */ jsx21(
7388
+ SpendStrip,
7389
+ {
7390
+ data: dailySpend,
7391
+ mtdSpend,
7392
+ loading: usageLoading,
7393
+ height: gaugeSize,
7394
+ usageUnavailable: viewState.usageUnavailable,
7395
+ maxBudget: user?.max_budget ?? void 0
7396
+ }
7397
+ ) }),
7233
7398
  /* @__PURE__ */ jsx21(Collapse4, { in: isExpanded, unmountOnExit: true, children: /* @__PURE__ */ jsx21(
7234
7399
  Box19,
7235
7400
  {