@acarmisc/backstage-plugin-litellm 0.46.0 → 0.47.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.
@@ -7,6 +7,7 @@ import { McpServerInfo, TeamInfo, TeamKeyUsage, TeamMemberUsage, UsageMetrics, V
7
7
  */
8
8
  export declare const MemberUsageCards: FC<{
9
9
  usage: TeamMemberUsage;
10
+ filterText?: string;
10
11
  }>;
11
12
  /**
12
13
  * A team's keys with the period's usage, for team leads. The backend never
@@ -14,6 +15,7 @@ export declare const MemberUsageCards: FC<{
14
15
  */
15
16
  export declare const TeamKeyUsageTable: FC<{
16
17
  usage: TeamKeyUsage;
18
+ filterText?: string;
17
19
  }>;
18
20
  interface TeamUsageProps {
19
21
  teams: TeamInfo[];
package/dist/index.cjs.js CHANGED
@@ -5060,7 +5060,7 @@ function budgetStatValue(hidden, budget) {
5060
5060
  if (budget > 0) return fmtUsd2(budget);
5061
5061
  return "Unlimited";
5062
5062
  }
5063
- var import_react10, import_Paper8, import_Box14, import_Typography12, import_Skeleton5, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton6, import_Button6, import_CircularProgress4, import_styles10, import_Tooltip5, import_icons_material7, import_recharts2, import_jsx_runtime15, fmtUsd2, monoSx, fmtShare, MemberUsageCards, TeamKeyUsageTable, TeamCard, TeamUsage;
5063
+ var import_react10, import_Paper8, import_Box14, import_Typography12, import_Skeleton5, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse2, import_IconButton6, import_Button6, import_TextField5, import_InputAdornment3, import_CircularProgress4, import_styles10, import_Tooltip5, import_icons_material7, import_recharts2, import_jsx_runtime15, fmtUsd2, monoSx, fmtShare, MemberUsageCards, TeamKeyUsageTable, TeamCard, TeamUsage;
5064
5064
  var init_TeamUsage = __esm({
5065
5065
  "src/components/TeamUsage.tsx"() {
5066
5066
  "use strict";
@@ -5079,6 +5079,8 @@ var init_TeamUsage = __esm({
5079
5079
  import_Collapse2 = __toESM(require("@mui/material/Collapse"));
5080
5080
  import_IconButton6 = __toESM(require("@mui/material/IconButton"));
5081
5081
  import_Button6 = __toESM(require("@mui/material/Button"));
5082
+ import_TextField5 = __toESM(require("@mui/material/TextField"));
5083
+ import_InputAdornment3 = __toESM(require("@mui/material/InputAdornment"));
5082
5084
  import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
5083
5085
  import_styles10 = require("@mui/material/styles");
5084
5086
  import_Tooltip5 = __toESM(require("@mui/material/Tooltip"));
@@ -5089,21 +5091,28 @@ var init_TeamUsage = __esm({
5089
5091
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
5090
5092
  monoSx = { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 };
5091
5093
  fmtShare = (pct) => `${pct.toFixed(pct > 0 && pct < 1 ? 1 : 0)}%`;
5092
- MemberUsageCards = ({ usage }) => {
5094
+ MemberUsageCards = ({ usage, filterText }) => {
5093
5095
  const hidden = usage.budget_hidden === true;
5094
5096
  if (!usage.members.length) {
5095
5097
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "No members or activity in this period." });
5096
5098
  }
5099
+ const query = (filterText ?? "").trim().toLowerCase();
5100
+ const matches = (m) => {
5101
+ if (!query) return true;
5102
+ const haystacks = m.user_id === null ? ["Unattributed"] : [m.display_name, m.user_email, m.user_id];
5103
+ return haystacks.some((h) => h && h.toLowerCase().includes(query));
5104
+ };
5105
+ const visible = usage.members.filter(matches);
5097
5106
  const people = usage.members.filter((m) => m.user_id !== null).length;
5098
5107
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5099
5108
  !hidden && people > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", sx: { display: "block", mb: 1 }, children: `Team spend ${fmtUsd2(usage.total_spend)} \xB7 avg ${fmtUsd2(usage.total_spend / people)} per member` }),
5100
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5109
+ visible.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", sx: { display: "block", py: 1 }, children: "No members match that filter." }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5101
5110
  import_Box14.default,
5102
5111
  {
5103
5112
  role: "list",
5104
5113
  "aria-label": "Usage by member",
5105
5114
  sx: { display: "grid", gap: 1.5, gridTemplateColumns: "repeat(auto-fill, minmax(230px, 1fr))" },
5106
- children: usage.members.map((m) => {
5115
+ children: visible.map((m) => {
5107
5116
  const name = m.display_name ?? m.user_email ?? m.user_id;
5108
5117
  let sub = null;
5109
5118
  if (m.display_name) sub = m.user_email ?? m.user_id;
@@ -5146,11 +5155,20 @@ var init_TeamUsage = __esm({
5146
5155
  )
5147
5156
  ] });
5148
5157
  };
5149
- TeamKeyUsageTable = ({ usage }) => {
5158
+ TeamKeyUsageTable = ({ usage, filterText }) => {
5150
5159
  const hidden = usage.budget_hidden === true;
5151
5160
  if (!usage.keys.length) {
5152
5161
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "This team has no keys." });
5153
5162
  }
5163
+ const query = (filterText ?? "").trim().toLowerCase();
5164
+ const visible = usage.keys.filter((k) => {
5165
+ if (!query) return true;
5166
+ const haystacks = [k.key_alias, k.key, k.user_email, k.user_id, k.display_name];
5167
+ return haystacks.some((h) => h && h.toLowerCase().includes(query));
5168
+ });
5169
+ if (visible.length === 0) {
5170
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", sx: { display: "block", py: 1 }, children: "No keys match that filter." });
5171
+ }
5154
5172
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableContainer4.default, { component: import_Paper8.default, variant: "outlined", sx: { borderRadius: 1.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Table4.default, { size: "small", sx: dataTableSx, "aria-label": "Keys by member", children: [
5155
5173
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableHead4.default, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5156
5174
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Key" }),
@@ -5162,7 +5180,7 @@ var init_TeamUsage = __esm({
5162
5180
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Tokens" }),
5163
5181
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Requests" })
5164
5182
  ] }) }),
5165
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableBody4.default, { children: usage.keys.map((k) => {
5183
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableBody4.default, { children: visible.map((k) => {
5166
5184
  const owner = k.display_name ?? k.user_email ?? k.user_id;
5167
5185
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5168
5186
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableCell4.default, { children: [
@@ -5188,7 +5206,9 @@ var init_TeamUsage = __esm({
5188
5206
  };
5189
5207
  TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, memberUsageFailed, keyUsage, keyUsageLoading, keyUsageFailed, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5190
5208
  const [expanded, setExpanded] = (0, import_react10.useState)(false);
5209
+ const [teamSearch, setTeamSearch] = (0, import_react10.useState)("");
5191
5210
  const chart = useChartTheme();
5211
+ const searchValue = teamSearch.trim() ? teamSearch : "";
5192
5212
  const handleExpandChange = (newExpanded) => {
5193
5213
  setExpanded(newExpanded);
5194
5214
  if (newExpanded && onExpand) {
@@ -5386,14 +5406,33 @@ var init_TeamUsage = __esm({
5386
5406
  renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5387
5407
  renderDailySpendSection(),
5388
5408
  (memberUsageLoading || memberUsage || memberUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5389
- sectionLabel("Usage by member"),
5390
- memberUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MemberUsageCards, { usage: memberUsage }),
5409
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 1, children: [
5410
+ sectionLabel("Usage by member"),
5411
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5412
+ import_TextField5.default,
5413
+ {
5414
+ size: "small",
5415
+ placeholder: "Filter members by name or email\u2026",
5416
+ value: teamSearch,
5417
+ onChange: (e) => setTeamSearch(e.target.value),
5418
+ sx: { width: { xs: "100%", sm: 260 } },
5419
+ InputProps: {
5420
+ startAdornment: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_InputAdornment3.default, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material7.Search, { fontSize: "small", color: "disabled" }) }),
5421
+ endAdornment: searchValue ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_InputAdornment3.default, { position: "end", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_IconButton6.default, { size: "small", onClick: () => setTeamSearch(""), "aria-label": "Clear member filter", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_icons_material7.Close, { fontSize: "small" }) }) }) : void 0
5422
+ }
5423
+ }
5424
+ )
5425
+ ] }),
5426
+ memberUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MemberUsageCards, { usage: memberUsage, filterText: searchValue }),
5391
5427
  !memberUsage && memberUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-member usage" }),
5392
5428
  !memberUsage && !memberUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_CircularProgress4.default, { size: 22 }) })
5393
5429
  ] }),
5394
5430
  (keyUsageLoading || keyUsage || keyUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5395
- sectionLabel("Keys by member"),
5396
- keyUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TeamKeyUsageTable, { usage: keyUsage }),
5431
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 1, children: [
5432
+ sectionLabel("Keys by member"),
5433
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", noWrap: true, children: "Filter member keys, owner or alias" })
5434
+ ] }),
5435
+ keyUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TeamKeyUsageTable, { usage: keyUsage, filterText: searchValue }),
5397
5436
  !keyUsage && keyUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-key usage" }),
5398
5437
  !keyUsage && !keyUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_CircularProgress4.default, { size: 22 }) })
5399
5438
  ] }),
@@ -5558,7 +5597,7 @@ function fmtTokens(n) {
5558
5597
  if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
5559
5598
  return String(n);
5560
5599
  }
5561
- var import_react11, import_Box15, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography13, import_MenuItem5, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment3, import_icons_material8, import_ContentCopy, import_Tooltip6, import_Snackbar, import_Alert4, import_styles11, import_jsx_runtime16, ALL_PROXY_MODELS, ModelsTable;
5600
+ var import_react11, import_Box15, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography13, import_MenuItem5, import_Select, import_FormControl, import_InputLabel, import_TextField6, import_InputAdornment4, import_icons_material8, import_ContentCopy, import_Tooltip6, import_Snackbar, import_Alert4, import_styles11, import_jsx_runtime16, ALL_PROXY_MODELS, ModelsTable;
5562
5601
  var init_ModelsTable = __esm({
5563
5602
  "src/components/ModelsTable.tsx"() {
5564
5603
  "use strict";
@@ -5576,8 +5615,8 @@ var init_ModelsTable = __esm({
5576
5615
  import_Select = __toESM(require("@mui/material/Select"));
5577
5616
  import_FormControl = __toESM(require("@mui/material/FormControl"));
5578
5617
  import_InputLabel = __toESM(require("@mui/material/InputLabel"));
5579
- import_TextField5 = __toESM(require("@mui/material/TextField"));
5580
- import_InputAdornment3 = __toESM(require("@mui/material/InputAdornment"));
5618
+ import_TextField6 = __toESM(require("@mui/material/TextField"));
5619
+ import_InputAdornment4 = __toESM(require("@mui/material/InputAdornment"));
5581
5620
  import_icons_material8 = require("@mui/icons-material");
5582
5621
  import_ContentCopy = __toESM(require("@mui/icons-material/ContentCopy"));
5583
5622
  import_Tooltip6 = __toESM(require("@mui/material/Tooltip"));
@@ -5695,7 +5734,7 @@ var init_ModelsTable = __esm({
5695
5734
  )
5696
5735
  ] }),
5697
5736
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
5698
- import_TextField5.default,
5737
+ import_TextField6.default,
5699
5738
  {
5700
5739
  label: "Filter Models",
5701
5740
  size: "small",
@@ -5704,7 +5743,7 @@ var init_ModelsTable = __esm({
5704
5743
  disabled: !selectedTeamId,
5705
5744
  sx: { minWidth: 240, mb: 2 },
5706
5745
  InputProps: {
5707
- startAdornment: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_InputAdornment3.default, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_icons_material8.Search, { fontSize: "small" }) })
5746
+ startAdornment: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_InputAdornment4.default, { position: "start", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_icons_material8.Search, { fontSize: "small" }) })
5708
5747
  }
5709
5748
  }
5710
5749
  )