@acarmisc/backstage-plugin-litellm 0.17.0 → 0.18.1

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
@@ -1920,7 +1920,7 @@ var init_GenerateKeyDialog = __esm({
1920
1920
  });
1921
1921
 
1922
1922
  // src/components/ManageTeamDialog.tsx
1923
- import React5, { useState as useState3, useEffect as useEffect2 } from "react";
1923
+ import React5, { useState as useState3, useEffect as useEffect2, useMemo as useMemo4 } from "react";
1924
1924
  import Dialog3 from "@mui/material/Dialog";
1925
1925
  import DialogTitle3 from "@mui/material/DialogTitle";
1926
1926
  import DialogContent3 from "@mui/material/DialogContent";
@@ -1931,19 +1931,29 @@ 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 CircularProgress3 from "@mui/material/CircularProgress";
1934
1935
  import MenuItem2 from "@mui/material/MenuItem";
1935
1936
  import Box5 from "@mui/material/Box";
1936
1937
  import Divider2 from "@mui/material/Divider";
1937
1938
  import Typography5 from "@mui/material/Typography";
1938
1939
  import IconButton3 from "@mui/material/IconButton";
1939
- import List from "@mui/material/List";
1940
- import ListItem from "@mui/material/ListItem";
1941
- import ListItemText from "@mui/material/ListItemText";
1940
+ import Paper3 from "@mui/material/Paper";
1941
+ import Table2 from "@mui/material/Table";
1942
+ import TableBody2 from "@mui/material/TableBody";
1943
+ import TableCell2 from "@mui/material/TableCell";
1944
+ import TableContainer2 from "@mui/material/TableContainer";
1945
+ import TableHead2 from "@mui/material/TableHead";
1946
+ import TableRow2 from "@mui/material/TableRow";
1942
1947
  import { Delete as DeleteIcon } from "@mui/icons-material";
1948
+ import { useApi } from "@backstage/core-plugin-api";
1949
+ import { catalogApiRef } from "@backstage/plugin-catalog-react";
1950
+ import { stringifyEntityRef } from "@backstage/catalog-model";
1951
+ import { useAsync } from "react-use";
1943
1952
  var BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
1944
1953
  var init_ManageTeamDialog = __esm({
1945
1954
  "src/components/ManageTeamDialog.tsx"() {
1946
1955
  "use strict";
1956
+ init_ui();
1947
1957
  BUDGET_DURATION_OPTIONS = [
1948
1958
  { value: "1d", label: "Daily (1d)" },
1949
1959
  { value: "7d", label: "Weekly (7d)" },
@@ -1978,6 +1988,7 @@ var init_ManageTeamDialog = __esm({
1978
1988
  const [budgetDuration, setBudgetDuration] = useState3("");
1979
1989
  const [submitting, setSubmitting] = useState3(false);
1980
1990
  const [error, setError] = useState3(null);
1991
+ const [memberQuery, setMemberQuery] = useState3("");
1981
1992
  const [memberRef, setMemberRef] = useState3("");
1982
1993
  const [memberBudget, setMemberBudget] = useState3("");
1983
1994
  const [memberBusy, setMemberBusy] = useState3(false);
@@ -1990,6 +2001,36 @@ var init_ManageTeamDialog = __esm({
1990
2001
  const [mcpError, setMcpError] = useState3(null);
1991
2002
  const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
1992
2003
  const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
2004
+ const members = useMemo4(() => team?.members_with_roles ?? [], [team]);
2005
+ const showMembers = mode === "edit" && !!canManageMembers;
2006
+ const catalogApi = useApi(catalogApiRef);
2007
+ const { value: catalogUsers, loading: usersLoading } = useAsync(async () => {
2008
+ if (!open || !showMembers) return [];
2009
+ try {
2010
+ const { items } = await catalogApi.getEntities({
2011
+ filter: { kind: "User" },
2012
+ fields: ["kind", "metadata.namespace", "metadata.name", "metadata.title", "spec.profile"]
2013
+ });
2014
+ return items;
2015
+ } catch {
2016
+ return [];
2017
+ }
2018
+ }, [open, showMembers, catalogApi]);
2019
+ const userOptions = useMemo4(() => {
2020
+ const existingEmails = new Set(
2021
+ members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
2022
+ );
2023
+ return (catalogUsers ?? []).map((e) => {
2024
+ const ref = stringifyEntityRef(e);
2025
+ const displayName = e.spec?.profile?.displayName || e.metadata.title || e.metadata.name;
2026
+ const email = e.spec?.profile?.email;
2027
+ return {
2028
+ ref,
2029
+ email,
2030
+ label: email ? `${displayName} (${email})` : displayName
2031
+ };
2032
+ }).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
2033
+ }, [catalogUsers, members]);
1993
2034
  useEffect2(() => {
1994
2035
  if (open) {
1995
2036
  if (mode === "edit" && team) {
@@ -2008,6 +2049,7 @@ var init_ManageTeamDialog = __esm({
2008
2049
  setBudgetDuration(DEFAULT_BUDGET_DURATION);
2009
2050
  }
2010
2051
  setError(null);
2052
+ setMemberQuery("");
2011
2053
  setMemberRef("");
2012
2054
  setMemberBudget("");
2013
2055
  setMemberError(null);
@@ -2079,6 +2121,7 @@ var init_ManageTeamDialog = __esm({
2079
2121
  try {
2080
2122
  setMemberBusy(true);
2081
2123
  await onAddMember(memberRef.trim(), budget);
2124
+ setMemberQuery("");
2082
2125
  setMemberRef("");
2083
2126
  setMemberBudget("");
2084
2127
  } catch (err) {
@@ -2125,8 +2168,6 @@ var init_ManageTeamDialog = __esm({
2125
2168
  };
2126
2169
  const modelNames = allModels.map((m) => m.model_name);
2127
2170
  const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
2128
- const members = team?.members_with_roles ?? [];
2129
- const showMembers = mode === "edit" && !!canManageMembers;
2130
2171
  const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
2131
2172
  const kbOptions = (vectorStores ?? []).map((v) => v.id);
2132
2173
  const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
@@ -2150,7 +2191,15 @@ var init_ManageTeamDialog = __esm({
2150
2191
  value: models,
2151
2192
  onChange: (_, newValue) => setModels(newValue),
2152
2193
  disabled: submitting,
2153
- renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Models" })
2194
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(
2195
+ TextField3,
2196
+ {
2197
+ ...params,
2198
+ label: "Models",
2199
+ required: true,
2200
+ helperText: "At least one model is required here \u2014 this delegated flow only grants access to models your admin has allow-listed for team creation, not every proxy model. (Teams with no model restriction shown elsewhere were configured directly in LiteLLM.)"
2201
+ }
2202
+ )
2154
2203
  }
2155
2204
  ), /* @__PURE__ */ React5.createElement(
2156
2205
  TextField3,
@@ -2182,34 +2231,69 @@ var init_ManageTeamDialog = __esm({
2182
2231
  fullWidth: true
2183
2232
  },
2184
2233
  durationOptions.map((o) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
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(
2186
- ListItem,
2234
+ ), 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", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ React5.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ React5.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React5.createElement(TableHead2, null, /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, null, "User"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Role"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ React5.createElement(TableBody2, null, members.map((m) => /* @__PURE__ */ React5.createElement(TableRow2, { key: m.user_id }, /* @__PURE__ */ React5.createElement(TableCell2, null, m.user_email ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2" }, m.user_email), /* @__PURE__ */ React5.createElement(
2235
+ Typography5,
2187
2236
  {
2188
- key: m.user_id,
2189
- disableGutters: true,
2190
- secondaryAction: /* @__PURE__ */ React5.createElement(
2191
- IconButton3,
2192
- {
2193
- edge: "end",
2194
- size: "small",
2195
- "aria-label": `remove ${m.user_id}`,
2196
- disabled: memberBusy,
2197
- onClick: () => handleRemoveMember(m.user_id)
2198
- },
2199
- /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2200
- )
2237
+ variant: "caption",
2238
+ color: "text.secondary",
2239
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2201
2240
  },
2202
- /* @__PURE__ */ React5.createElement(ListItemText, { primary: m.user_id, secondary: m.role })
2203
- ))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2204
- TextField3,
2241
+ m.user_id
2242
+ )) : /* @__PURE__ */ React5.createElement(
2243
+ Typography5,
2205
2244
  {
2206
- label: "Backstage user ref",
2207
- placeholder: "user:default/alice",
2208
- value: memberRef,
2209
- onChange: (e) => setMemberRef(e.target.value),
2210
- disabled: memberBusy,
2245
+ variant: "body2",
2246
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2247
+ },
2248
+ m.user_id
2249
+ )), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React5.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React5.createElement(
2250
+ IconButton3,
2251
+ {
2252
+ edge: "end",
2211
2253
  size: "small",
2212
- sx: { flex: 1 }
2254
+ "aria-label": `remove ${m.user_id}`,
2255
+ disabled: memberBusy,
2256
+ onClick: () => handleRemoveMember(m.user_id)
2257
+ },
2258
+ /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2259
+ ))))))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ React5.createElement(
2260
+ Autocomplete3,
2261
+ {
2262
+ freeSolo: true,
2263
+ autoHighlight: true,
2264
+ options: userOptions,
2265
+ loading: usersLoading,
2266
+ inputValue: memberQuery,
2267
+ onInputChange: (_, newInputValue, reason) => {
2268
+ setMemberQuery(newInputValue);
2269
+ if (reason === "input" || reason === "clear") {
2270
+ setMemberRef(newInputValue);
2271
+ }
2272
+ },
2273
+ onChange: (_, newValue) => {
2274
+ if (newValue && typeof newValue !== "string") {
2275
+ setMemberQuery(newValue.label);
2276
+ setMemberRef(newValue.ref);
2277
+ }
2278
+ },
2279
+ getOptionLabel: (o) => typeof o === "string" ? o : o.label,
2280
+ isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
2281
+ disabled: memberBusy,
2282
+ sx: { flex: 1, minWidth: 260 },
2283
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(
2284
+ TextField3,
2285
+ {
2286
+ ...params,
2287
+ label: "Add member",
2288
+ placeholder: "Search by name or email\u2026",
2289
+ size: "small",
2290
+ helperText: "Pick a catalog user, or paste a user entity ref",
2291
+ InputProps: {
2292
+ ...params.InputProps,
2293
+ endAdornment: /* @__PURE__ */ React5.createElement(React5.Fragment, null, usersLoading ? /* @__PURE__ */ React5.createElement(CircularProgress3, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
2294
+ }
2295
+ }
2296
+ )
2213
2297
  }
2214
2298
  ), /* @__PURE__ */ React5.createElement(
2215
2299
  TextField3,
@@ -2275,19 +2359,19 @@ var init_ManageTeamDialog = __esm({
2275
2359
  });
2276
2360
 
2277
2361
  // src/components/UsageStats.tsx
2278
- import React6, { useMemo as useMemo4, useState as useState4 } from "react";
2279
- import Paper3 from "@mui/material/Paper";
2362
+ import React6, { useMemo as useMemo5, useState as useState4 } from "react";
2363
+ import Paper4 from "@mui/material/Paper";
2280
2364
  import Box6 from "@mui/material/Box";
2281
2365
  import Typography6 from "@mui/material/Typography";
2282
2366
  import TextField4 from "@mui/material/TextField";
2283
2367
  import MenuItem3 from "@mui/material/MenuItem";
2284
2368
  import Grid from "@mui/material/Grid";
2285
- import Table2 from "@mui/material/Table";
2286
- import TableBody2 from "@mui/material/TableBody";
2287
- import TableCell2 from "@mui/material/TableCell";
2288
- import TableContainer2 from "@mui/material/TableContainer";
2289
- import TableHead2 from "@mui/material/TableHead";
2290
- import TableRow2 from "@mui/material/TableRow";
2369
+ import Table3 from "@mui/material/Table";
2370
+ import TableBody3 from "@mui/material/TableBody";
2371
+ import TableCell3 from "@mui/material/TableCell";
2372
+ import TableContainer3 from "@mui/material/TableContainer";
2373
+ import TableHead3 from "@mui/material/TableHead";
2374
+ import TableRow3 from "@mui/material/TableRow";
2291
2375
  import Skeleton3 from "@mui/material/Skeleton";
2292
2376
  import {
2293
2377
  AreaChart,
@@ -2368,7 +2452,7 @@ var init_UsageStats = __esm({
2368
2452
  }
2369
2453
  onDateRangeChange({ start, end }, preset);
2370
2454
  };
2371
- const dailyData = useMemo4(
2455
+ const dailyData = useMemo5(
2372
2456
  () => (usage?.daily_usage ?? []).map((d) => ({
2373
2457
  date: d.date,
2374
2458
  spend: d.spend,
@@ -2381,21 +2465,21 @@ var init_UsageStats = __esm({
2381
2465
  })),
2382
2466
  [usage]
2383
2467
  );
2384
- const cumulativeData = useMemo4(() => {
2468
+ const cumulativeData = useMemo5(() => {
2385
2469
  let cum = 0;
2386
2470
  return dailyData.map((d) => {
2387
2471
  cum += d.spend;
2388
2472
  return { date: d.date, cumulative: cum };
2389
2473
  });
2390
2474
  }, [dailyData]);
2391
- const successRateData = useMemo4(
2475
+ const successRateData = useMemo5(
2392
2476
  () => dailyData.filter((d) => d.apiRequests > 0).map((d) => ({
2393
2477
  date: d.date,
2394
2478
  successRate: parseFloat((d.successfulRequests / d.apiRequests * 100).toFixed(1))
2395
2479
  })),
2396
2480
  [dailyData]
2397
2481
  );
2398
- const { modelSpendByDate, topModels: topSpendModels } = useMemo4(() => {
2482
+ const { modelSpendByDate, topModels: topSpendModels } = useMemo5(() => {
2399
2483
  const rows = usage?.daily_by_model ?? [];
2400
2484
  const modelTotals = {};
2401
2485
  for (const r of rows) modelTotals[r.model] = (modelTotals[r.model] ?? 0) + r.spend;
@@ -2410,7 +2494,7 @@ var init_UsageStats = __esm({
2410
2494
  const hasOther = sorted.some((r) => r.Other > 0);
2411
2495
  return { modelSpendByDate: sorted, topModels: hasOther ? [...top, "Other"] : top };
2412
2496
  }, [usage]);
2413
- const modelRows = useMemo4(() => {
2497
+ const modelRows = useMemo5(() => {
2414
2498
  const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
2415
2499
  model,
2416
2500
  spend: d.total_spend,
@@ -2424,11 +2508,11 @@ var init_UsageStats = __esm({
2424
2508
  }));
2425
2509
  return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
2426
2510
  }, [usage, selectedModel]);
2427
- const topModelSpendBars = useMemo4(
2511
+ const topModelSpendBars = useMemo5(
2428
2512
  () => [...modelRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2429
2513
  [modelRows]
2430
2514
  );
2431
- const keyRows = useMemo4(
2515
+ const keyRows = useMemo5(
2432
2516
  () => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
2433
2517
  keyHash,
2434
2518
  keyAlias: d.key_alias ?? keyHash.slice(0, 8),
@@ -2445,7 +2529,7 @@ var init_UsageStats = __esm({
2445
2529
  })),
2446
2530
  [usage]
2447
2531
  );
2448
- const topKeySpendBars = useMemo4(
2532
+ const topKeySpendBars = useMemo5(
2449
2533
  () => [...keyRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2450
2534
  [keyRows]
2451
2535
  );
@@ -2483,21 +2567,21 @@ var init_UsageStats = __esm({
2483
2567
  ];
2484
2568
  const renderKeyRows = () => {
2485
2569
  if (loading) {
2486
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2570
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2487
2571
  }
2488
2572
  if (keyRows.length === 0) {
2489
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2573
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2490
2574
  }
2491
- return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React6.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2575
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React6.createElement(TableRow3, { key: r.keyHash }, /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React6.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2492
2576
  };
2493
2577
  const renderModelRows = () => {
2494
2578
  if (loading) {
2495
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2579
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2496
2580
  }
2497
2581
  if (modelRows.length === 0) {
2498
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2582
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2499
2583
  }
2500
- return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2584
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React6.createElement(TableRow3, { key: r.model }, /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2501
2585
  };
2502
2586
  const periodSelect = /* @__PURE__ */ React6.createElement(
2503
2587
  TextField4,
@@ -2650,7 +2734,7 @@ var init_UsageStats = __esm({
2650
2734
  height: Math.max(200, topModelSpendBars.length * 34)
2651
2735
  },
2652
2736
  /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2653
- ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Input"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Output"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderModelRows()))))),
2737
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead3, null, /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, null, "Model"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Success"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Input"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Output"), /* @__PURE__ */ React6.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody3, null, renderModelRows()))))),
2654
2738
  tab === "keys" && /* @__PURE__ */ React6.createElement(Grid, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ React6.createElement(
2655
2739
  ChartOrFallback,
2656
2740
  {
@@ -2659,7 +2743,7 @@ var init_UsageStats = __esm({
2659
2743
  height: Math.max(160, topKeySpendBars.length * 32)
2660
2744
  },
2661
2745
  /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2662
- ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React6.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderKeyRows())))))
2746
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead3, null, /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, null, "Key"), /* @__PURE__ */ React6.createElement(TableCell3, null, "Models"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Tokens"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody3, null, renderKeyRows())))))
2663
2747
  );
2664
2748
  };
2665
2749
  }
@@ -2667,23 +2751,23 @@ var init_UsageStats = __esm({
2667
2751
 
2668
2752
  // src/components/TeamUsage.tsx
2669
2753
  import React7, { useState as useState5 } from "react";
2670
- import Paper4 from "@mui/material/Paper";
2754
+ import Paper5 from "@mui/material/Paper";
2671
2755
  import Box7 from "@mui/material/Box";
2672
2756
  import Typography7 from "@mui/material/Typography";
2673
2757
  import Skeleton4 from "@mui/material/Skeleton";
2674
2758
  import Divider3 from "@mui/material/Divider";
2675
- import Table3 from "@mui/material/Table";
2676
- import TableBody3 from "@mui/material/TableBody";
2677
- import TableCell3 from "@mui/material/TableCell";
2678
- import TableHead3 from "@mui/material/TableHead";
2679
- import TableRow3 from "@mui/material/TableRow";
2680
- import TableContainer3 from "@mui/material/TableContainer";
2759
+ import Table4 from "@mui/material/Table";
2760
+ import TableBody4 from "@mui/material/TableBody";
2761
+ import TableCell4 from "@mui/material/TableCell";
2762
+ import TableHead4 from "@mui/material/TableHead";
2763
+ import TableRow4 from "@mui/material/TableRow";
2764
+ import TableContainer4 from "@mui/material/TableContainer";
2681
2765
  import Collapse from "@mui/material/Collapse";
2682
2766
  import IconButton4 from "@mui/material/IconButton";
2683
2767
  import Button5 from "@mui/material/Button";
2684
- import CircularProgress3 from "@mui/material/CircularProgress";
2768
+ import CircularProgress4 from "@mui/material/CircularProgress";
2685
2769
  import { alpha as alpha4 } from "@mui/material/styles";
2686
- import { ExpandMore as ExpandMore2, Group } from "@mui/icons-material";
2770
+ import { ExpandMore as ExpandMore2, Group, Add as Add3 } from "@mui/icons-material";
2687
2771
  import {
2688
2772
  AreaChart as AreaChart2,
2689
2773
  Area as Area2,
@@ -2715,7 +2799,7 @@ var init_TeamUsage = __esm({
2715
2799
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
2716
2800
  const renderDailySpendSection = () => {
2717
2801
  if (usageLoading) {
2718
- return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress3, { size: 22 }));
2802
+ return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress4, { size: 22 }));
2719
2803
  }
2720
2804
  if (dailyData.length === 0) return null;
2721
2805
  return /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, /* @__PURE__ */ React7.createElement(
@@ -2754,7 +2838,7 @@ var init_TeamUsage = __esm({
2754
2838
  },
2755
2839
  label
2756
2840
  );
2757
- return /* @__PURE__ */ React7.createElement(Paper4, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2841
+ return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2758
2842
  Box7,
2759
2843
  {
2760
2844
  sx: (theme) => ({
@@ -2819,13 +2903,13 @@ var init_TeamUsage = __esm({
2819
2903
  /* @__PURE__ */ React7.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2820
2904
  /* @__PURE__ */ React7.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2821
2905
  ), budget > 0 && /* @__PURE__ */ React7.createElement(Box7, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React7.createElement(Meter, { value: budgetPct, tone: budgetTone2(isOver, isNear), height: 5 })), /* @__PURE__ */ React7.createElement(Collapse, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React7.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React7.createElement(Box7, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React7.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React7.createElement(Box7, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React7.createElement(
2822
- TableContainer3,
2906
+ TableContainer4,
2823
2907
  {
2824
- component: Paper4,
2908
+ component: Paper5,
2825
2909
  variant: "outlined",
2826
2910
  sx: { borderRadius: 1.5 }
2827
2911
  },
2828
- /* @__PURE__ */ React7.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead3, null, /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, null, "User"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2912
+ /* @__PURE__ */ React7.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead4, null, /* @__PURE__ */ React7.createElement(TableRow4, null, /* @__PURE__ */ React7.createElement(TableCell4, null, "User"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2829
2913
  Typography7,
2830
2914
  {
2831
2915
  variant: "caption",
@@ -2840,7 +2924,7 @@ var init_TeamUsage = __esm({
2840
2924
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2841
2925
  },
2842
2926
  m.user_id
2843
- )), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React7.createElement(
2927
+ )), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(
2844
2928
  StatusPill,
2845
2929
  {
2846
2930
  label: m.role,
@@ -2856,13 +2940,26 @@ var init_TeamUsage = __esm({
2856
2940
  getTeamUsage,
2857
2941
  getTeamUsageLoading,
2858
2942
  canManage,
2859
- onEditTeam
2943
+ onEditTeam,
2944
+ canCreate,
2945
+ onCreateTeam
2860
2946
  }) => {
2947
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React7.createElement(
2948
+ Button5,
2949
+ {
2950
+ variant: "contained",
2951
+ color: "primary",
2952
+ disableElevation: true,
2953
+ startIcon: /* @__PURE__ */ React7.createElement(Add3, null),
2954
+ onClick: onCreateTeam
2955
+ },
2956
+ "Create Team"
2957
+ ) : void 0;
2861
2958
  if (loading) {
2862
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2959
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2863
2960
  }
2864
2961
  if (!teams.length) {
2865
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(
2962
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(
2866
2963
  EmptyState,
2867
2964
  {
2868
2965
  message: "You're not a member of any LiteLLM team yet.",
@@ -2870,30 +2967,38 @@ var init_TeamUsage = __esm({
2870
2967
  }
2871
2968
  ));
2872
2969
  }
2873
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}` }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
2874
- TeamCard,
2970
+ return /* @__PURE__ */ React7.createElement(
2971
+ SectionCard,
2875
2972
  {
2876
- key: team.team_id,
2877
- team,
2878
- usage: getTeamUsage(team.team_id),
2879
- usageLoading: getTeamUsageLoading(team.team_id),
2880
- canManage,
2881
- onEditTeam
2882
- }
2883
- ))));
2973
+ title: "Teams",
2974
+ subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
2975
+ actions: createAction
2976
+ },
2977
+ /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
2978
+ TeamCard,
2979
+ {
2980
+ key: team.team_id,
2981
+ team,
2982
+ usage: getTeamUsage(team.team_id),
2983
+ usageLoading: getTeamUsageLoading(team.team_id),
2984
+ canManage,
2985
+ onEditTeam
2986
+ }
2987
+ )))
2988
+ );
2884
2989
  };
2885
2990
  }
2886
2991
  });
2887
2992
 
2888
2993
  // src/components/ModelsTable.tsx
2889
- import React8, { useState as useState6, useMemo as useMemo5, useCallback } from "react";
2994
+ import React8, { useState as useState6, useMemo as useMemo6, useCallback } from "react";
2890
2995
  import Box8 from "@mui/material/Box";
2891
- import Table4 from "@mui/material/Table";
2892
- import TableBody4 from "@mui/material/TableBody";
2893
- import TableCell4 from "@mui/material/TableCell";
2894
- import TableContainer4 from "@mui/material/TableContainer";
2895
- import TableHead4 from "@mui/material/TableHead";
2896
- import TableRow4 from "@mui/material/TableRow";
2996
+ import Table5 from "@mui/material/Table";
2997
+ import TableBody5 from "@mui/material/TableBody";
2998
+ import TableCell5 from "@mui/material/TableCell";
2999
+ import TableContainer5 from "@mui/material/TableContainer";
3000
+ import TableHead5 from "@mui/material/TableHead";
3001
+ import TableRow5 from "@mui/material/TableRow";
2897
3002
  import Typography8 from "@mui/material/Typography";
2898
3003
  import MenuItem4 from "@mui/material/MenuItem";
2899
3004
  import Select from "@mui/material/Select";
@@ -2904,6 +3009,9 @@ import Tooltip3 from "@mui/material/Tooltip";
2904
3009
  import Snackbar from "@mui/material/Snackbar";
2905
3010
  import Alert3 from "@mui/material/Alert";
2906
3011
  import { alpha as alpha5, useTheme as useTheme2 } from "@mui/material/styles";
3012
+ import TextField5 from "@mui/material/TextField";
3013
+ import InputAdornment2 from "@mui/material/InputAdornment";
3014
+ import { Search as Search2 } from "@mui/icons-material";
2907
3015
  function isModelAllowedByTeam2(model, teamModels) {
2908
3016
  if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
2909
3017
  return teamModels.includes(model.model_name) || !!model.access_groups?.some((group) => teamModels.includes(group));
@@ -2930,15 +3038,23 @@ var init_ModelsTable = __esm({
2930
3038
  ModelsTable = ({ allModels, teams, loading }) => {
2931
3039
  const theme = useTheme2();
2932
3040
  const [selectedTeamId, setSelectedTeamId] = useState6("");
3041
+ const [filterText, setFilterText] = useState6("");
2933
3042
  const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
2934
- const selectedTeam = useMemo5(
3043
+ const selectedTeam = useMemo6(
2935
3044
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
2936
3045
  [teams, selectedTeamId]
2937
3046
  );
2938
- const filteredModels = useMemo5(() => {
3047
+ const filteredModels = useMemo6(() => {
2939
3048
  if (!selectedTeam) return [];
2940
- return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
2941
- }, [allModels, selectedTeam]);
3049
+ let result = allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3050
+ if (filterText.trim()) {
3051
+ const q = filterText.toLowerCase();
3052
+ result = result.filter(
3053
+ (model) => model.model_name.toLowerCase().includes(q) || (model.access_groups || []).some((group) => group.toLowerCase().includes(q))
3054
+ );
3055
+ }
3056
+ return result;
3057
+ }, [allModels, selectedTeam, filterText]);
2942
3058
  const handleCopyModelId = useCallback((modelId) => {
2943
3059
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
2944
3060
  }, []);
@@ -2971,7 +3087,19 @@ var init_ModelsTable = __esm({
2971
3087
  onChange: (e) => setSelectedTeamId(e.target.value)
2972
3088
  },
2973
3089
  teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2974
- ))),
3090
+ )), /* @__PURE__ */ React8.createElement(
3091
+ TextField5,
3092
+ {
3093
+ label: "Filter Models",
3094
+ size: "small",
3095
+ value: filterText,
3096
+ onChange: (e) => setFilterText(e.target.value),
3097
+ sx: { minWidth: 240, mb: 2 },
3098
+ InputProps: {
3099
+ startAdornment: /* @__PURE__ */ React8.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React8.createElement(Search2, { fontSize: "small" }))
3100
+ }
3101
+ }
3102
+ )),
2975
3103
  loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
2976
3104
  !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
2977
3105
  EmptyState,
@@ -2987,7 +3115,7 @@ var init_ModelsTable = __esm({
2987
3115
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
2988
3116
  }
2989
3117
  ),
2990
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer4, null, /* @__PURE__ */ React8.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead4, null, /* @__PURE__ */ React8.createElement(TableRow4, null, /* @__PURE__ */ React8.createElement(TableCell4, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody4, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow4, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
3118
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer5, null, /* @__PURE__ */ React8.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead5, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
2991
3119
  Box8,
2992
3120
  {
2993
3121
  component: "span",
@@ -2999,7 +3127,7 @@ var init_ModelsTable = __esm({
2999
3127
  style: { cursor: "pointer", display: "inline-flex" }
3000
3128
  },
3001
3129
  /* @__PURE__ */ React8.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3002
- )))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(
3130
+ )))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(
3003
3131
  Box8,
3004
3132
  {
3005
3133
  component: "span",
@@ -3016,7 +3144,7 @@ var init_ModelsTable = __esm({
3016
3144
  }
3017
3145
  },
3018
3146
  m.mode
3019
- )), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3147
+ )), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3020
3148
  /* @__PURE__ */ React8.createElement(
3021
3149
  Snackbar,
3022
3150
  {
@@ -3036,14 +3164,14 @@ var init_ModelsTable = __esm({
3036
3164
  import React9, { useState as useState7, useCallback as useCallback2 } from "react";
3037
3165
  import Box9 from "@mui/material/Box";
3038
3166
  import Typography9 from "@mui/material/Typography";
3039
- import Table5 from "@mui/material/Table";
3040
- import TableBody5 from "@mui/material/TableBody";
3041
- import TableCell5 from "@mui/material/TableCell";
3042
- import TableContainer5 from "@mui/material/TableContainer";
3043
- import TableHead5 from "@mui/material/TableHead";
3044
- import TableRow5 from "@mui/material/TableRow";
3167
+ import Table6 from "@mui/material/Table";
3168
+ import TableBody6 from "@mui/material/TableBody";
3169
+ import TableCell6 from "@mui/material/TableCell";
3170
+ import TableContainer6 from "@mui/material/TableContainer";
3171
+ import TableHead6 from "@mui/material/TableHead";
3172
+ import TableRow6 from "@mui/material/TableRow";
3045
3173
  import TablePagination from "@mui/material/TablePagination";
3046
- import TextField5 from "@mui/material/TextField";
3174
+ import TextField6 from "@mui/material/TextField";
3047
3175
  import MenuItem5 from "@mui/material/MenuItem";
3048
3176
  import Skeleton5 from "@mui/material/Skeleton";
3049
3177
  import Collapse2 from "@mui/material/Collapse";
@@ -3051,7 +3179,7 @@ import IconButton5 from "@mui/material/IconButton";
3051
3179
  import Alert4 from "@mui/material/Alert";
3052
3180
  import { alpha as alpha6 } from "@mui/material/styles";
3053
3181
  import { KeyboardArrowDown } from "@mui/icons-material";
3054
- import { useAsync } from "react-use";
3182
+ import { useAsync as useAsync2 } from "react-use";
3055
3183
  function actionTone(action) {
3056
3184
  if (!action) return "neutral";
3057
3185
  for (const [key, tone] of Object.entries(ACTION_TONES)) {
@@ -3072,10 +3200,10 @@ function formatDateTime(iso) {
3072
3200
  }
3073
3201
  function renderAuditLogBody(loading, entries) {
3074
3202
  if (loading) {
3075
- return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3203
+ return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3076
3204
  }
3077
3205
  if (entries.length === 0) {
3078
- return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
3206
+ return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
3079
3207
  EmptyState,
3080
3208
  {
3081
3209
  message: "No audit events found",
@@ -3107,7 +3235,7 @@ var init_AuditLog = __esm({
3107
3235
  DetailRow = ({ entry }) => {
3108
3236
  const [open, setOpen] = useState7(false);
3109
3237
  const hasDetail = entry.before_value || entry.updated_values;
3110
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3238
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3111
3239
  IconButton5,
3112
3240
  {
3113
3241
  size: "small",
@@ -3121,7 +3249,7 @@ var init_AuditLog = __esm({
3121
3249
  })
3122
3250
  },
3123
3251
  /* @__PURE__ */ React9.createElement(KeyboardArrowDown, { fontSize: "small" })
3124
- )), /* @__PURE__ */ React9.createElement(TableCell5, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(
3252
+ )), /* @__PURE__ */ React9.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(
3125
3253
  Typography9,
3126
3254
  {
3127
3255
  variant: "body2",
@@ -3131,7 +3259,7 @@ var init_AuditLog = __esm({
3131
3259
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3132
3260
  },
3133
3261
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3134
- )), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow5, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3262
+ )), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3135
3263
  Box9,
3136
3264
  {
3137
3265
  display: "flex",
@@ -3176,7 +3304,7 @@ var init_AuditLog = __esm({
3176
3304
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3177
3305
  [page, pageSize, filters]
3178
3306
  )();
3179
- const { value, loading, error } = useAsync(
3307
+ const { value, loading, error } = useAsync2(
3180
3308
  () => api.getAuditLogs(fetchParams),
3181
3309
  [api, fetchParams]
3182
3310
  );
@@ -3189,7 +3317,7 @@ var init_AuditLog = __esm({
3189
3317
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3190
3318
  flush: true,
3191
3319
  actions: /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
3192
- TextField5,
3320
+ TextField6,
3193
3321
  {
3194
3322
  size: "small",
3195
3323
  label: "Action",
@@ -3207,7 +3335,7 @@ var init_AuditLog = __esm({
3207
3335
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3208
3336
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3209
3337
  ), /* @__PURE__ */ React9.createElement(
3210
- TextField5,
3338
+ TextField6,
3211
3339
  {
3212
3340
  size: "small",
3213
3341
  label: "Table",
@@ -3226,7 +3354,7 @@ var init_AuditLog = __esm({
3226
3354
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3227
3355
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3228
3356
  ), /* @__PURE__ */ React9.createElement(
3229
- TextField5,
3357
+ TextField6,
3230
3358
  {
3231
3359
  size: "small",
3232
3360
  label: "Changed by",
@@ -3240,7 +3368,7 @@ var init_AuditLog = __esm({
3240
3368
  ))
3241
3369
  },
3242
3370
  error && /* @__PURE__ */ React9.createElement(Box9, { px: 2.5, pb: 2 }, /* @__PURE__ */ React9.createElement(Alert4, { severity: "error" }, error.message)),
3243
- /* @__PURE__ */ React9.createElement(TableContainer5, null, /* @__PURE__ */ React9.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead5, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell5, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody5, null, renderAuditLogBody(loading, entries)))),
3371
+ /* @__PURE__ */ React9.createElement(TableContainer6, null, /* @__PURE__ */ React9.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead6, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
3244
3372
  /* @__PURE__ */ React9.createElement(
3245
3373
  TablePagination,
3246
3374
  {
@@ -3296,18 +3424,17 @@ var LiteLLMPage_exports = {};
3296
3424
  __export(LiteLLMPage_exports, {
3297
3425
  LiteLLMPage: () => LiteLLMPage
3298
3426
  });
3299
- import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo6 } from "react";
3427
+ import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo7 } from "react";
3300
3428
  import Box10 from "@mui/material/Box";
3301
3429
  import Snackbar2 from "@mui/material/Snackbar";
3302
3430
  import Alert5 from "@mui/material/Alert";
3303
- import CircularProgress4 from "@mui/material/CircularProgress";
3431
+ import CircularProgress5 from "@mui/material/CircularProgress";
3304
3432
  import Typography10 from "@mui/material/Typography";
3305
- import Paper5 from "@mui/material/Paper";
3433
+ import Paper6 from "@mui/material/Paper";
3306
3434
  import Tabs2 from "@mui/material/Tabs";
3307
3435
  import Tab2 from "@mui/material/Tab";
3308
- import Button6 from "@mui/material/Button";
3309
- import { useAsync as useAsync2, useAsyncRetry } from "react-use";
3310
- import { useApi } from "@backstage/core-plugin-api";
3436
+ import { useAsync as useAsync3, useAsyncRetry } from "react-use";
3437
+ import { useApi as useApi2 } from "@backstage/core-plugin-api";
3311
3438
  import { usePermission } from "@backstage/plugin-permission-react";
3312
3439
  function initDateRange() {
3313
3440
  let preset = "7d";
@@ -3339,7 +3466,7 @@ var init_LiteLLMPage = __esm({
3339
3466
  init_permissions();
3340
3467
  PERIOD_LS_KEY2 = "litellm_usage_period";
3341
3468
  LiteLLMPage = () => {
3342
- const api = useApi(liteLlmApiRef);
3469
+ const api = useApi2(liteLlmApiRef);
3343
3470
  const [dateRange, setDateRange] = useState8(initDateRange);
3344
3471
  const [currentPreset, setCurrentPreset] = useState8(() => {
3345
3472
  try {
@@ -3354,7 +3481,7 @@ var init_LiteLLMPage = __esm({
3354
3481
  const [manageTeam, setManageTeam] = useState8(null);
3355
3482
  const [teamUsageCache, setTeamUsageCache] = useState8({});
3356
3483
  const [teamUsageLoading, setTeamUsageLoading] = useState8({});
3357
- const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
3484
+ const { value: userInfo, loading: userLoading, error: userError } = useAsync3(
3358
3485
  () => api.getUserInfo(),
3359
3486
  [api]
3360
3487
  );
@@ -3369,7 +3496,7 @@ var init_LiteLLMPage = __esm({
3369
3496
  },
3370
3497
  [api]
3371
3498
  );
3372
- const { value: allModels, loading: modelsLoading } = useAsync2(
3499
+ const { value: allModels, loading: modelsLoading } = useAsync3(
3373
3500
  () => api.listModels().catch(() => []),
3374
3501
  [api]
3375
3502
  );
@@ -3377,8 +3504,8 @@ var init_LiteLLMPage = __esm({
3377
3504
  () => api.getTeams().catch(() => []),
3378
3505
  [api]
3379
3506
  );
3380
- const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
3381
- const { value: managedTeams } = useAsync2(
3507
+ const { value: liteLlmConfig } = useAsync3(() => api.getConfig(), [api]);
3508
+ const { value: managedTeams } = useAsync3(
3382
3509
  async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
3383
3510
  [api, liteLlmConfig]
3384
3511
  );
@@ -3389,15 +3516,15 @@ var init_LiteLLMPage = __esm({
3389
3516
  const { allowed: canManageMcpServers } = usePermission({ permission: litellmTeamMcpManagePermission });
3390
3517
  const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
3391
3518
  const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
3392
- const { value: vectorStores } = useAsync2(
3519
+ const { value: vectorStores } = useAsync3(
3393
3520
  async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
3394
3521
  [api, objectPermsEnabled]
3395
3522
  );
3396
- const { value: mcpServers } = useAsync2(
3523
+ const { value: mcpServers } = useAsync3(
3397
3524
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3398
3525
  [api, objectPermsEnabled]
3399
3526
  );
3400
- const teams = useMemo6(() => {
3527
+ const teams = useMemo7(() => {
3401
3528
  if (!allTeams?.length) return [];
3402
3529
  if (!userInfo) return allTeams;
3403
3530
  const userId = userInfo.user_id;
@@ -3409,7 +3536,7 @@ var init_LiteLLMPage = __esm({
3409
3536
  );
3410
3537
  return byMembership.length > 0 ? byMembership : allTeams;
3411
3538
  }, [allTeams, userInfo]);
3412
- const { value: usage, loading: usageLoading } = useAsync2(async () => {
3539
+ const { value: usage, loading: usageLoading } = useAsync3(async () => {
3413
3540
  const startDate = dateRange.start.toISOString().split("T")[0];
3414
3541
  const endDate = dateRange.end.toISOString().split("T")[0];
3415
3542
  return api.getUsage(startDate, endDate);
@@ -3436,7 +3563,7 @@ var init_LiteLLMPage = __esm({
3436
3563
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
3437
3564
  }
3438
3565
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
3439
- const allowedModels = useMemo6(() => {
3566
+ const allowedModels = useMemo7(() => {
3440
3567
  if (!allModels?.length) return [];
3441
3568
  const userModels = userInfo?.models;
3442
3569
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -3544,12 +3671,12 @@ var init_LiteLLMPage = __esm({
3544
3671
  );
3545
3672
  const isInitialLoading = userLoading && !userInfo;
3546
3673
  if (isInitialLoading) {
3547
- return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress4, null));
3674
+ return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress5, null));
3548
3675
  }
3549
3676
  if (userError || !userInfo) {
3550
3677
  const isProvisioningEnabled = userError?.body?.provisioning === true;
3551
3678
  const hint = userError?.body?.hint;
3552
- return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper5, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
3679
+ return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper6, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
3553
3680
  }
3554
3681
  const pageTabs = /* @__PURE__ */ React10.createElement(
3555
3682
  Tabs2,
@@ -3610,14 +3737,7 @@ var init_LiteLLMPage = __esm({
3610
3737
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
3611
3738
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
3612
3739
  const visibleTeams = Array.from(teamsById.values());
3613
- return /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teamMgmtEnabled && canCreateTeam && /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", justifyContent: "flex-end" } }, /* @__PURE__ */ React10.createElement(
3614
- Button6,
3615
- {
3616
- variant: "contained",
3617
- onClick: () => setManageTeam({ mode: "create" })
3618
- },
3619
- "Create Team"
3620
- )), /* @__PURE__ */ React10.createElement(
3740
+ return /* @__PURE__ */ React10.createElement(
3621
3741
  TeamUsage,
3622
3742
  {
3623
3743
  teams: visibleTeams,
@@ -3628,9 +3748,11 @@ var init_LiteLLMPage = __esm({
3628
3748
  },
3629
3749
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
3630
3750
  canManage: teamMgmtEnabled && canManageTeam,
3631
- onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
3751
+ onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
3752
+ canCreate: teamMgmtEnabled && canCreateTeam,
3753
+ onCreateTeam: () => setManageTeam({ mode: "create" })
3632
3754
  }
3633
- ));
3755
+ );
3634
3756
  })(), activeTab === "models" && /* @__PURE__ */ React10.createElement(
3635
3757
  ModelsTable,
3636
3758
  {
@@ -3771,17 +3893,17 @@ init_TeamUsage();
3771
3893
  init_api();
3772
3894
  init_format();
3773
3895
  import React12, { useState as useState9, useEffect as useEffect3 } from "react";
3774
- import Paper6 from "@mui/material/Paper";
3896
+ import Paper7 from "@mui/material/Paper";
3775
3897
  import Box11 from "@mui/material/Box";
3776
3898
  import Typography11 from "@mui/material/Typography";
3777
3899
  import FormControl2 from "@mui/material/FormControl";
3778
3900
  import Select2 from "@mui/material/Select";
3779
3901
  import MenuItem6 from "@mui/material/MenuItem";
3780
3902
  import Grid2 from "@mui/material/Grid";
3781
- import CircularProgress5 from "@mui/material/CircularProgress";
3903
+ import CircularProgress6 from "@mui/material/CircularProgress";
3782
3904
  import Alert6 from "@mui/material/Alert";
3783
3905
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
3784
- import { useApi as useApi2 } from "@backstage/core-plugin-api";
3906
+ import { useApi as useApi3 } from "@backstage/core-plugin-api";
3785
3907
  function presetToDateRange(preset) {
3786
3908
  const end = /* @__PURE__ */ new Date();
3787
3909
  const start = /* @__PURE__ */ new Date();
@@ -3799,7 +3921,7 @@ var LiteLLMHomeWidget = ({
3799
3921
  defaultPeriod = "7d",
3800
3922
  title = "LiteLLM Usage"
3801
3923
  }) => {
3802
- const api = useApi2(liteLlmApiRef);
3924
+ const api = useApi3(liteLlmApiRef);
3803
3925
  const [period, setPeriod] = useState9(defaultPeriod);
3804
3926
  const [loading, setLoading] = useState9(true);
3805
3927
  const [usageError, setUsageError] = useState9(null);
@@ -3843,7 +3965,7 @@ var LiteLLMHomeWidget = ({
3843
3965
  spend: d.spend
3844
3966
  }));
3845
3967
  const hasSparkline = dailyData.length > 0;
3846
- return /* @__PURE__ */ React12.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
3968
+ return /* @__PURE__ */ React12.createElement(Paper7, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
3847
3969
  Select2,
3848
3970
  {
3849
3971
  value: period,
@@ -3853,7 +3975,7 @@ var LiteLLMHomeWidget = ({
3853
3975
  /* @__PURE__ */ React12.createElement(MenuItem6, { value: "today" }, "Today"),
3854
3976
  /* @__PURE__ */ React12.createElement(MenuItem6, { value: "7d" }, "7d"),
3855
3977
  /* @__PURE__ */ React12.createElement(MenuItem6, { value: "30d" }, "30d")
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(
3978
+ ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress6, { 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(
3857
3979
  Area3,
3858
3980
  {
3859
3981
  type: "monotone",