@acarmisc/backstage-plugin-litellm 0.21.1 → 0.21.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/index.esm.js CHANGED
@@ -2845,6 +2845,7 @@ import IconButton4 from "@mui/material/IconButton";
2845
2845
  import { ExpandMore as ExpandMore2 } from "@mui/icons-material";
2846
2846
  import { alpha as alpha4 } from "@mui/material/styles";
2847
2847
  import { useApi as useApi2 } from "@backstage/core-plugin-api";
2848
+ import { Link } from "@backstage/core-components";
2848
2849
  function levelToneColor(tone, theme) {
2849
2850
  switch (tone) {
2850
2851
  case "success":
@@ -2859,7 +2860,7 @@ function levelToneColor(tone, theme) {
2859
2860
  return theme.palette.primary.main;
2860
2861
  }
2861
2862
  }
2862
- var LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
2863
+ var LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
2863
2864
  var init_LiteLLMBudgetWidget = __esm({
2864
2865
  "src/components/LiteLLMBudgetWidget.tsx"() {
2865
2866
  "use strict";
@@ -2976,6 +2977,21 @@ var init_LiteLLMBudgetWidget = __esm({
2976
2977
  user: "personal",
2977
2978
  team: "team"
2978
2979
  };
2980
+ KEYS_PATH = "/litellm?tab=keys";
2981
+ MoreKeysNote = ({ count }) => /* @__PURE__ */ React7.createElement(Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
2982
+ HierarchyNote = ({ withReset }) => /* @__PURE__ */ React7.createElement(
2983
+ Box7,
2984
+ {
2985
+ sx: (theme) => ({
2986
+ px: 1.5,
2987
+ py: 1.25,
2988
+ borderRadius: 1.5,
2989
+ border: "1px dashed",
2990
+ borderColor: alpha4(theme.palette.text.primary, 0.18)
2991
+ })
2992
+ },
2993
+ /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ React7.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
2994
+ );
2979
2995
  TaggedLimit = ({ limit }) => {
2980
2996
  const tone = budgetTone2(limit.pct);
2981
2997
  return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(
@@ -3005,7 +3021,7 @@ var init_LiteLLMBudgetWidget = __esm({
3005
3021
  },
3006
3022
  !hasBudgetedKeys && /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
3007
3023
  summary.keys.map((k) => /* @__PURE__ */ React7.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
3008
- summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, "+", summary.hiddenBudgetedKeys, " more budgeted key", summary.hiddenBudgetedKeys > 1 ? "s" : "", " further from the cap")
3024
+ summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
3009
3025
  ), /* @__PURE__ */ React7.createElement(
3010
3026
  LevelFrame,
3011
3027
  {
@@ -3015,27 +3031,14 @@ var init_LiteLLMBudgetWidget = __esm({
3015
3031
  note: "skipped for team-bound keys"
3016
3032
  },
3017
3033
  summary.user ? /* @__PURE__ */ React7.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
3018
- ), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React7.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React7.createElement(
3019
- Box7,
3020
- {
3021
- sx: (theme) => ({
3022
- mt: 2,
3023
- px: 1.5,
3024
- py: 1.25,
3025
- borderRadius: 1.5,
3026
- border: "1px dashed",
3027
- borderColor: alpha4(theme.palette.text.primary, 0.18)
3028
- })
3029
- },
3030
- /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", display: "block" }, "The first cap a request runs into is the one that stops it. A limit with a reset window (e.g. daily or every 30 days) clears its spend to $0 when the window closes; without a window it never resets.")
3031
- ));
3034
+ ), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ React7.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ React7.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ React7.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ React7.createElement(Box7, { sx: { mt: 2 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, { withReset: true })));
3032
3035
  };
3033
3036
  CompactBody = ({ summary }) => {
3034
3037
  const { count } = budgetHeadline(summary);
3035
3038
  if (count === 0) {
3036
3039
  return /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
3037
3040
  }
3038
- return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary" }, "+", summary.hiddenBudgetedKeys, " more budgeted key", summary.hiddenBudgetedKeys > 1 ? "s" : "", " further from the cap"), summary.user && /* @__PURE__ */ React7.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3041
+ return /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ React7.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ React7.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ React7.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ React7.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
3039
3042
  };
3040
3043
  LiteLLMBudgetWidget = ({
3041
3044
  title = "Budget Policy",
@@ -3844,6 +3847,7 @@ import Paper7 from "@mui/material/Paper";
3844
3847
  import Tabs2 from "@mui/material/Tabs";
3845
3848
  import Tab2 from "@mui/material/Tab";
3846
3849
  import Grid2 from "@mui/material/Grid";
3850
+ import { useSearchParams } from "react-router-dom";
3847
3851
  import { useAsync as useAsync3, useAsyncRetry } from "react-use";
3848
3852
  import { useApi as useApi3 } from "@backstage/core-plugin-api";
3849
3853
  import { usePermission } from "@backstage/plugin-permission-react";
@@ -3861,7 +3865,7 @@ function initDateRange() {
3861
3865
  else start.setDate(start.getDate() - 7);
3862
3866
  return { start, end };
3863
3867
  }
3864
- var PERIOD_LS_KEY2, LiteLLMPage;
3868
+ var PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
3865
3869
  var init_LiteLLMPage = __esm({
3866
3870
  "src/components/LiteLLMPage.tsx"() {
3867
3871
  "use strict";
@@ -3877,6 +3881,8 @@ var init_LiteLLMPage = __esm({
3877
3881
  init_api();
3878
3882
  init_permissions();
3879
3883
  PERIOD_LS_KEY2 = "litellm_usage_period";
3884
+ PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
3885
+ isPageTab = (v) => !!v && PAGE_TABS.includes(v);
3880
3886
  LiteLLMPage = () => {
3881
3887
  const api = useApi3(liteLlmApiRef);
3882
3888
  const [dateRange, setDateRange] = useState9(initDateRange);
@@ -3887,7 +3893,25 @@ var init_LiteLLMPage = __esm({
3887
3893
  return "7d";
3888
3894
  }
3889
3895
  });
3890
- const [activeTab, setActiveTab] = useState9("overview");
3896
+ const [searchParams, setSearchParams] = useSearchParams();
3897
+ const [activeTab, setActiveTab] = useState9(() => {
3898
+ const t = searchParams.get("tab");
3899
+ return isPageTab(t) ? t : "overview";
3900
+ });
3901
+ const selectTab = useCallback3(
3902
+ (tab) => {
3903
+ setActiveTab(tab);
3904
+ setSearchParams(
3905
+ (prev) => {
3906
+ const next = new URLSearchParams(prev);
3907
+ next.set("tab", tab);
3908
+ return next;
3909
+ },
3910
+ { replace: true }
3911
+ );
3912
+ },
3913
+ [setSearchParams]
3914
+ );
3891
3915
  const [snackbar, setSnackbar] = useState9(null);
3892
3916
  const [generateDialogOpen, setGenerateDialogOpen] = useState9(false);
3893
3917
  const [manageTeam, setManageTeam] = useState9(null);
@@ -4094,7 +4118,7 @@ var init_LiteLLMPage = __esm({
4094
4118
  Tabs2,
4095
4119
  {
4096
4120
  value: activeTab,
4097
- onChange: (_, v) => setActiveTab(v),
4121
+ onChange: (_, v) => selectTab(v),
4098
4122
  variant: "scrollable",
4099
4123
  scrollButtons: "auto",
4100
4124
  sx: {