@acarmisc/backstage-plugin-litellm 0.16.0 → 0.17.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
@@ -1931,6 +1931,7 @@ import Alert2 from "@mui/material/Alert";
1931
1931
  import Checkbox2 from "@mui/material/Checkbox";
1932
1932
  import FormControlLabel2 from "@mui/material/FormControlLabel";
1933
1933
  import Autocomplete3 from "@mui/material/Autocomplete";
1934
+ import MenuItem2 from "@mui/material/MenuItem";
1934
1935
  import Box5 from "@mui/material/Box";
1935
1936
  import Divider2 from "@mui/material/Divider";
1936
1937
  import Typography5 from "@mui/material/Typography";
@@ -1939,10 +1940,19 @@ import List from "@mui/material/List";
1939
1940
  import ListItem from "@mui/material/ListItem";
1940
1941
  import ListItemText from "@mui/material/ListItemText";
1941
1942
  import { Delete as DeleteIcon } from "@mui/icons-material";
1942
- var ManageTeamDialog;
1943
+ var BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
1943
1944
  var init_ManageTeamDialog = __esm({
1944
1945
  "src/components/ManageTeamDialog.tsx"() {
1945
1946
  "use strict";
1947
+ BUDGET_DURATION_OPTIONS = [
1948
+ { value: "1d", label: "Daily (1d)" },
1949
+ { value: "7d", label: "Weekly (7d)" },
1950
+ { value: "30d", label: "Monthly (30d)" },
1951
+ { value: "90d", label: "Quarterly (90d)" },
1952
+ { value: "365d", label: "Yearly (365d)" }
1953
+ ];
1954
+ DEFAULT_BUDGET_DURATION = "30d";
1955
+ FALLBACK_BUDGET_CEILING = 1e3;
1946
1956
  ManageTeamDialog = ({
1947
1957
  open,
1948
1958
  onClose,
@@ -1979,21 +1989,23 @@ var init_ManageTeamDialog = __esm({
1979
1989
  const [mcpBusy, setMcpBusy] = useState3(false);
1980
1990
  const [mcpError, setMcpError] = useState3(null);
1981
1991
  const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
1982
- const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling;
1992
+ const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
1983
1993
  useEffect2(() => {
1984
1994
  if (open) {
1985
1995
  if (mode === "edit" && team) {
1986
1996
  setAlias(team.team_alias ?? "");
1987
1997
  setModels(team.models ?? []);
1988
- setMaxBudget(team.max_budget ? String(team.max_budget) : "");
1998
+ setMaxBudget(
1999
+ team.max_budget ? String(team.max_budget) : String(maxBudgetCeiling)
2000
+ );
1989
2001
  setUnlimited(allowUnlimitedBudget && !team.max_budget);
1990
- setBudgetDuration("");
2002
+ setBudgetDuration(team.budget_duration ?? DEFAULT_BUDGET_DURATION);
1991
2003
  } else {
1992
2004
  setAlias("");
1993
2005
  setModels([]);
1994
- setMaxBudget("");
2006
+ setMaxBudget(String(maxBudgetCeiling));
1995
2007
  setUnlimited(allowUnlimitedBudget);
1996
- setBudgetDuration("");
2008
+ setBudgetDuration(DEFAULT_BUDGET_DURATION);
1997
2009
  }
1998
2010
  setError(null);
1999
2011
  setMemberRef("");
@@ -2004,7 +2016,7 @@ var init_ManageTeamDialog = __esm({
2004
2016
  setMcpIds(team?.object_permission?.mcp_servers ?? []);
2005
2017
  setMcpError(null);
2006
2018
  }
2007
- }, [open, mode, team, allowUnlimitedBudget]);
2019
+ }, [open, mode, team, allowUnlimitedBudget, maxBudgetCeiling]);
2008
2020
  const handleSubmit = async () => {
2009
2021
  setError(null);
2010
2022
  if (!alias.trim()) {
@@ -2025,7 +2037,7 @@ var init_ManageTeamDialog = __esm({
2025
2037
  setError("Budget must be a positive number");
2026
2038
  return;
2027
2039
  }
2028
- if (maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 && budget > maxBudgetCeiling) {
2040
+ if (budget > maxBudgetCeiling) {
2029
2041
  setError(`Budget cannot exceed $${maxBudgetCeiling}`);
2030
2042
  return;
2031
2043
  }
@@ -2112,6 +2124,7 @@ var init_ManageTeamDialog = __esm({
2112
2124
  }
2113
2125
  };
2114
2126
  const modelNames = allModels.map((m) => m.model_name);
2127
+ const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
2115
2128
  const members = team?.members_with_roles ?? [];
2116
2129
  const showMembers = mode === "edit" && !!canManageMembers;
2117
2130
  const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
@@ -2147,7 +2160,8 @@ var init_ManageTeamDialog = __esm({
2147
2160
  value: maxBudget,
2148
2161
  onChange: (e) => setMaxBudget(e.target.value),
2149
2162
  disabled: submitting || unlimited,
2150
- helperText: maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 ? `Maximum: $${maxBudgetCeiling}` : void 0,
2163
+ inputProps: { min: 0, max: maxBudgetCeiling },
2164
+ helperText: `Maximum: $${maxBudgetCeiling}`,
2151
2165
  fullWidth: true
2152
2166
  }
2153
2167
  ), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
@@ -2159,13 +2173,15 @@ var init_ManageTeamDialog = __esm({
2159
2173
  ), /* @__PURE__ */ React5.createElement(
2160
2174
  TextField3,
2161
2175
  {
2176
+ select: true,
2162
2177
  label: "Budget Duration",
2163
2178
  value: budgetDuration,
2164
2179
  onChange: (e) => setBudgetDuration(e.target.value),
2165
- placeholder: "30d",
2166
- disabled: submitting,
2180
+ disabled: submitting || unlimited,
2181
+ helperText: "Spend-reset period for the team budget",
2167
2182
  fullWidth: true
2168
- }
2183
+ },
2184
+ durationOptions.map((o) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
2169
2185
  ), showMembers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary" }, "No members yet.") : /* @__PURE__ */ React5.createElement(List, { dense: true, disablePadding: true }, members.map((m) => /* @__PURE__ */ React5.createElement(
2170
2186
  ListItem,
2171
2187
  {
@@ -2264,7 +2280,7 @@ import Paper3 from "@mui/material/Paper";
2264
2280
  import Box6 from "@mui/material/Box";
2265
2281
  import Typography6 from "@mui/material/Typography";
2266
2282
  import TextField4 from "@mui/material/TextField";
2267
- import MenuItem2 from "@mui/material/MenuItem";
2283
+ import MenuItem3 from "@mui/material/MenuItem";
2268
2284
  import Grid from "@mui/material/Grid";
2269
2285
  import Table2 from "@mui/material/Table";
2270
2286
  import TableBody2 from "@mui/material/TableBody";
@@ -2493,7 +2509,7 @@ var init_UsageStats = __esm({
2493
2509
  onChange: (e) => handlePresetChange(e.target.value),
2494
2510
  sx: { minWidth: 160 }
2495
2511
  },
2496
- Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: p, value: p }, PRESET_LABELS[p]))
2512
+ Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: p, value: p }, PRESET_LABELS[p]))
2497
2513
  );
2498
2514
  return /* @__PURE__ */ React6.createElement(
2499
2515
  SectionCard,
@@ -2538,8 +2554,8 @@ var init_UsageStats = __esm({
2538
2554
  onChange: (e) => setSelectedModel(e.target.value),
2539
2555
  sx: { minWidth: 220 }
2540
2556
  },
2541
- /* @__PURE__ */ React6.createElement(MenuItem2, { value: "all" }, "All models"),
2542
- models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
2557
+ /* @__PURE__ */ React6.createElement(MenuItem3, { value: "all" }, "All models"),
2558
+ models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: m.model_name, value: m.model_name }, m.model_name))
2543
2559
  )
2544
2560
  ),
2545
2561
  tab === "costs" && /* @__PURE__ */ React6.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ React6.createElement(
@@ -2879,7 +2895,7 @@ import TableContainer4 from "@mui/material/TableContainer";
2879
2895
  import TableHead4 from "@mui/material/TableHead";
2880
2896
  import TableRow4 from "@mui/material/TableRow";
2881
2897
  import Typography8 from "@mui/material/Typography";
2882
- import MenuItem3 from "@mui/material/MenuItem";
2898
+ import MenuItem4 from "@mui/material/MenuItem";
2883
2899
  import Select from "@mui/material/Select";
2884
2900
  import FormControl from "@mui/material/FormControl";
2885
2901
  import InputLabel from "@mui/material/InputLabel";
@@ -2954,7 +2970,7 @@ var init_ModelsTable = __esm({
2954
2970
  label: "Team",
2955
2971
  onChange: (e) => setSelectedTeamId(e.target.value)
2956
2972
  },
2957
- teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2973
+ teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2958
2974
  ))),
2959
2975
  loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
2960
2976
  !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
@@ -3028,7 +3044,7 @@ import TableHead5 from "@mui/material/TableHead";
3028
3044
  import TableRow5 from "@mui/material/TableRow";
3029
3045
  import TablePagination from "@mui/material/TablePagination";
3030
3046
  import TextField5 from "@mui/material/TextField";
3031
- import MenuItem4 from "@mui/material/MenuItem";
3047
+ import MenuItem5 from "@mui/material/MenuItem";
3032
3048
  import Skeleton5 from "@mui/material/Skeleton";
3033
3049
  import Collapse2 from "@mui/material/Collapse";
3034
3050
  import IconButton5 from "@mui/material/IconButton";
@@ -3185,11 +3201,11 @@ var init_AuditLog = __esm({
3185
3201
  },
3186
3202
  sx: { minWidth: 150 }
3187
3203
  },
3188
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All actions"),
3189
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "created" }, "Created"),
3190
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "updated" }, "Updated"),
3191
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "deleted" }, "Deleted"),
3192
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "blocked" }, "Blocked")
3204
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All actions"),
3205
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "created" }, "Created"),
3206
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "updated" }, "Updated"),
3207
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3208
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3193
3209
  ), /* @__PURE__ */ React9.createElement(
3194
3210
  TextField5,
3195
3211
  {
@@ -3203,12 +3219,12 @@ var init_AuditLog = __esm({
3203
3219
  },
3204
3220
  sx: { minWidth: 150 }
3205
3221
  },
3206
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All tables"),
3207
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_VerificationToken" }, "Key"),
3208
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamTable" }, "Team"),
3209
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamMembership" }, "Team member"),
3210
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3211
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_UserTable" }, "User")
3222
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All tables"),
3223
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3224
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3225
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3226
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3227
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3212
3228
  ), /* @__PURE__ */ React9.createElement(
3213
3229
  TextField5,
3214
3230
  {
@@ -3760,7 +3776,7 @@ import Box11 from "@mui/material/Box";
3760
3776
  import Typography11 from "@mui/material/Typography";
3761
3777
  import FormControl2 from "@mui/material/FormControl";
3762
3778
  import Select2 from "@mui/material/Select";
3763
- import MenuItem5 from "@mui/material/MenuItem";
3779
+ import MenuItem6 from "@mui/material/MenuItem";
3764
3780
  import Grid2 from "@mui/material/Grid";
3765
3781
  import CircularProgress5 from "@mui/material/CircularProgress";
3766
3782
  import Alert6 from "@mui/material/Alert";
@@ -3834,9 +3850,9 @@ var LiteLLMHomeWidget = ({
3834
3850
  onChange: (e) => setPeriod(e.target.value),
3835
3851
  displayEmpty: true
3836
3852
  },
3837
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "today" }, "Today"),
3838
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "7d" }, "7d"),
3839
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "30d" }, "30d")
3853
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "today" }, "Today"),
3854
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "7d" }, "7d"),
3855
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "30d" }, "30d")
3840
3856
  ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress5, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React12.createElement(React12.Fragment, null, partialFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React12.createElement(Box11, { height: 120 }, /* @__PURE__ */ React12.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React12.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React12.createElement(
3841
3857
  Area3,
3842
3858
  {