@acarmisc/backstage-plugin-litellm 0.45.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.
package/dist/index.esm.js CHANGED
@@ -5056,10 +5056,12 @@ import TableContainer4 from "@mui/material/TableContainer";
5056
5056
  import Collapse2 from "@mui/material/Collapse";
5057
5057
  import IconButton6 from "@mui/material/IconButton";
5058
5058
  import Button6 from "@mui/material/Button";
5059
+ import TextField5 from "@mui/material/TextField";
5060
+ import InputAdornment3 from "@mui/material/InputAdornment";
5059
5061
  import CircularProgress4 from "@mui/material/CircularProgress";
5060
5062
  import { alpha as alpha10 } from "@mui/material/styles";
5061
5063
  import MuiTooltip from "@mui/material/Tooltip";
5062
- import { ExpandMore as ExpandMore3, Group, Add as Add2, LockOutlined, GroupAddOutlined } from "@mui/icons-material";
5064
+ import { ExpandMore as ExpandMore3, Group, Add as Add2, LockOutlined, GroupAddOutlined, Search as SearchIcon, Close as CloseIcon } from "@mui/icons-material";
5063
5065
  import {
5064
5066
  AreaChart as AreaChart2,
5065
5067
  Area as Area2,
@@ -5084,53 +5086,92 @@ function budgetStatValue(hidden, budget) {
5084
5086
  if (budget > 0) return fmtUsd2(budget);
5085
5087
  return "Unlimited";
5086
5088
  }
5087
- var fmtUsd2, monoSx, MemberUsageTable, TeamKeyUsageTable, TeamCard, TeamUsage;
5089
+ var fmtUsd2, monoSx, fmtShare, MemberUsageCards, TeamKeyUsageTable, TeamCard, TeamUsage;
5088
5090
  var init_TeamUsage = __esm({
5089
5091
  "src/components/TeamUsage.tsx"() {
5090
5092
  "use strict";
5091
5093
  init_ui();
5092
5094
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
5093
5095
  monoSx = { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 };
5094
- MemberUsageTable = ({ usage }) => {
5096
+ fmtShare = (pct) => `${pct.toFixed(pct > 0 && pct < 1 ? 1 : 0)}%`;
5097
+ MemberUsageCards = ({ usage, filterText }) => {
5095
5098
  const hidden = usage.budget_hidden === true;
5096
5099
  if (!usage.members.length) {
5097
5100
  return /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "No members or activity in this period." });
5098
5101
  }
5099
- return /* @__PURE__ */ jsx15(TableContainer4, { component: Paper8, variant: "outlined", sx: { borderRadius: 1.5 }, children: /* @__PURE__ */ jsxs14(Table4, { size: "small", sx: dataTableSx, "aria-label": "Usage by member", children: [
5100
- /* @__PURE__ */ jsx15(TableHead4, { children: /* @__PURE__ */ jsxs14(TableRow4, { children: [
5101
- /* @__PURE__ */ jsx15(TableCell4, { children: "Member" }),
5102
- !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Spend" }),
5103
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Share" }),
5104
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Tokens" }),
5105
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Requests" }),
5106
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Success" }),
5107
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Keys" })
5108
- ] }) }),
5109
- /* @__PURE__ */ jsx15(TableBody4, { children: usage.members.map((m) => {
5110
- const name = m.display_name ?? m.user_email ?? m.user_id;
5111
- let sub = null;
5112
- if (m.display_name) sub = m.user_email ?? m.user_id;
5113
- else if (m.user_email && m.user_id !== m.user_email) sub = m.user_id;
5114
- return /* @__PURE__ */ jsxs14(TableRow4, { children: [
5115
- /* @__PURE__ */ jsx15(TableCell4, { children: m.user_id === null ? /* @__PURE__ */ jsx15(MuiTooltip, { title: "Activity of keys that were deleted or have no owner", children: /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: "Unattributed" }) }) : /* @__PURE__ */ jsxs14(Fragment8, { children: [
5116
- /* @__PURE__ */ jsx15(Typography12, { variant: "body2", children: name }),
5117
- sub && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", sx: monoSx, children: sub })
5118
- ] }) }),
5119
- !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtUsd2(m.spend) }),
5120
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: `${m.spend_share_pct.toFixed(m.spend_share_pct > 0 && m.spend_share_pct < 1 ? 1 : 0)}%` }),
5121
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtCompact(m.total_tokens) }),
5122
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.api_requests.toLocaleString() }),
5123
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.success_rate === null ? "\u2014" : `${m.success_rate.toFixed(0)}%` }),
5124
- /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.user_id === null ? "\u2014" : m.key_count })
5125
- ] }, m.user_id ?? "__unattributed");
5126
- }) })
5127
- ] }) });
5102
+ const query = (filterText ?? "").trim().toLowerCase();
5103
+ const matches = (m) => {
5104
+ if (!query) return true;
5105
+ const haystacks = m.user_id === null ? ["Unattributed"] : [m.display_name, m.user_email, m.user_id];
5106
+ return haystacks.some((h) => h && h.toLowerCase().includes(query));
5107
+ };
5108
+ const visible = usage.members.filter(matches);
5109
+ const people = usage.members.filter((m) => m.user_id !== null).length;
5110
+ return /* @__PURE__ */ jsxs14(Box14, { children: [
5111
+ !hidden && people > 0 && /* @__PURE__ */ jsx15(Typography12, { 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` }),
5112
+ visible.length === 0 ? /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", sx: { display: "block", py: 1 }, children: "No members match that filter." }) : /* @__PURE__ */ jsx15(
5113
+ Box14,
5114
+ {
5115
+ role: "list",
5116
+ "aria-label": "Usage by member",
5117
+ sx: { display: "grid", gap: 1.5, gridTemplateColumns: "repeat(auto-fill, minmax(230px, 1fr))" },
5118
+ children: visible.map((m) => {
5119
+ const name = m.display_name ?? m.user_email ?? m.user_id;
5120
+ let sub = null;
5121
+ if (m.display_name) sub = m.user_email ?? m.user_id;
5122
+ else if (m.user_email && m.user_id !== m.user_email) sub = m.user_id;
5123
+ const stats = [
5124
+ ["Tokens", fmtCompact(m.total_tokens)],
5125
+ ["Requests", m.api_requests.toLocaleString()],
5126
+ ["Success", m.success_rate === null ? "\u2014" : `${m.success_rate.toFixed(0)}%`],
5127
+ ["Keys", m.user_id === null ? "\u2014" : String(m.key_count)]
5128
+ ];
5129
+ return /* @__PURE__ */ jsxs14(
5130
+ Paper8,
5131
+ {
5132
+ role: "listitem",
5133
+ variant: "outlined",
5134
+ sx: { borderRadius: 1.5, p: 1.5, minWidth: 0 },
5135
+ children: [
5136
+ /* @__PURE__ */ jsxs14(Box14, { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 1, children: [
5137
+ /* @__PURE__ */ jsx15(Box14, { minWidth: 0, children: m.user_id === null ? /* @__PURE__ */ jsx15(MuiTooltip, { title: "Activity of keys that were deleted or have no owner", children: /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: "Unattributed" }) }) : /* @__PURE__ */ jsxs14(Fragment8, { children: [
5138
+ /* @__PURE__ */ jsx15(Typography12, { variant: "body2", fontWeight: 600, noWrap: true, children: name }),
5139
+ sub && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", noWrap: true, sx: { ...monoSx, display: "block" }, children: sub })
5140
+ ] }) }),
5141
+ m.role && /* @__PURE__ */ jsx15(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })
5142
+ ] }),
5143
+ /* @__PURE__ */ jsxs14(Box14, { mt: 1.5, display: "flex", alignItems: "baseline", justifyContent: "space-between", children: [
5144
+ /* @__PURE__ */ jsx15(Typography12, { variant: "h6", component: "span", sx: { lineHeight: 1.2 }, children: hidden ? fmtShare(m.spend_share_pct) : fmtUsd2(m.spend) }),
5145
+ /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", children: hidden ? "of team usage" : `${fmtShare(m.spend_share_pct)} of team` })
5146
+ ] }),
5147
+ /* @__PURE__ */ jsx15(Box14, { mt: 0.75, children: /* @__PURE__ */ jsx15(Meter, { value: m.spend_share_pct, ariaLabel: `${name ?? "Unattributed"} share of team spend` }) }),
5148
+ /* @__PURE__ */ jsx15(Box14, { mt: 1.5, display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0.5, children: stats.map(([label, value]) => /* @__PURE__ */ jsxs14(Box14, { children: [
5149
+ /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", display: "block", children: label }),
5150
+ /* @__PURE__ */ jsx15(Typography12, { variant: "body2", children: value })
5151
+ ] }, label)) })
5152
+ ]
5153
+ },
5154
+ m.user_id ?? "__unattributed"
5155
+ );
5156
+ })
5157
+ }
5158
+ )
5159
+ ] });
5128
5160
  };
5129
- TeamKeyUsageTable = ({ usage }) => {
5161
+ TeamKeyUsageTable = ({ usage, filterText }) => {
5130
5162
  const hidden = usage.budget_hidden === true;
5131
5163
  if (!usage.keys.length) {
5132
5164
  return /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "This team has no keys." });
5133
5165
  }
5166
+ const query = (filterText ?? "").trim().toLowerCase();
5167
+ const visible = usage.keys.filter((k) => {
5168
+ if (!query) return true;
5169
+ const haystacks = [k.key_alias, k.key, k.user_email, k.user_id, k.display_name];
5170
+ return haystacks.some((h) => h && h.toLowerCase().includes(query));
5171
+ });
5172
+ if (visible.length === 0) {
5173
+ return /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", sx: { display: "block", py: 1 }, children: "No keys match that filter." });
5174
+ }
5134
5175
  return /* @__PURE__ */ jsx15(TableContainer4, { component: Paper8, variant: "outlined", sx: { borderRadius: 1.5 }, children: /* @__PURE__ */ jsxs14(Table4, { size: "small", sx: dataTableSx, "aria-label": "Keys by member", children: [
5135
5176
  /* @__PURE__ */ jsx15(TableHead4, { children: /* @__PURE__ */ jsxs14(TableRow4, { children: [
5136
5177
  /* @__PURE__ */ jsx15(TableCell4, { children: "Key" }),
@@ -5142,7 +5183,7 @@ var init_TeamUsage = __esm({
5142
5183
  /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Tokens" }),
5143
5184
  /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Requests" })
5144
5185
  ] }) }),
5145
- /* @__PURE__ */ jsx15(TableBody4, { children: usage.keys.map((k) => {
5186
+ /* @__PURE__ */ jsx15(TableBody4, { children: visible.map((k) => {
5146
5187
  const owner = k.display_name ?? k.user_email ?? k.user_id;
5147
5188
  return /* @__PURE__ */ jsxs14(TableRow4, { children: [
5148
5189
  /* @__PURE__ */ jsxs14(TableCell4, { children: [
@@ -5168,7 +5209,9 @@ var init_TeamUsage = __esm({
5168
5209
  };
5169
5210
  TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, memberUsageFailed, keyUsage, keyUsageLoading, keyUsageFailed, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5170
5211
  const [expanded, setExpanded] = useState9(false);
5212
+ const [teamSearch, setTeamSearch] = useState9("");
5171
5213
  const chart = useChartTheme();
5214
+ const searchValue = teamSearch.trim() ? teamSearch : "";
5172
5215
  const handleExpandChange = (newExpanded) => {
5173
5216
  setExpanded(newExpanded);
5174
5217
  if (newExpanded && onExpand) {
@@ -5366,18 +5409,37 @@ var init_TeamUsage = __esm({
5366
5409
  renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5367
5410
  renderDailySpendSection(),
5368
5411
  (memberUsageLoading || memberUsage || memberUsageFailed) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5369
- sectionLabel("Usage by member"),
5370
- memberUsage && /* @__PURE__ */ jsx15(MemberUsageTable, { usage: memberUsage }),
5412
+ /* @__PURE__ */ jsxs14(Box14, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 1, children: [
5413
+ sectionLabel("Usage by member"),
5414
+ /* @__PURE__ */ jsx15(
5415
+ TextField5,
5416
+ {
5417
+ size: "small",
5418
+ placeholder: "Filter members by name or email\u2026",
5419
+ value: teamSearch,
5420
+ onChange: (e) => setTeamSearch(e.target.value),
5421
+ sx: { width: { xs: "100%", sm: 260 } },
5422
+ InputProps: {
5423
+ startAdornment: /* @__PURE__ */ jsx15(InputAdornment3, { position: "start", children: /* @__PURE__ */ jsx15(SearchIcon, { fontSize: "small", color: "disabled" }) }),
5424
+ endAdornment: searchValue ? /* @__PURE__ */ jsx15(InputAdornment3, { position: "end", children: /* @__PURE__ */ jsx15(IconButton6, { size: "small", onClick: () => setTeamSearch(""), "aria-label": "Clear member filter", children: /* @__PURE__ */ jsx15(CloseIcon, { fontSize: "small" }) }) }) : void 0
5425
+ }
5426
+ }
5427
+ )
5428
+ ] }),
5429
+ memberUsage && /* @__PURE__ */ jsx15(MemberUsageCards, { usage: memberUsage, filterText: searchValue }),
5371
5430
  !memberUsage && memberUsageFailed && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", children: "Could not load per-member usage" }),
5372
5431
  !memberUsage && !memberUsageFailed && /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5373
5432
  ] }),
5374
5433
  (keyUsageLoading || keyUsage || keyUsageFailed) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5375
- sectionLabel("Keys by member"),
5376
- keyUsage && /* @__PURE__ */ jsx15(TeamKeyUsageTable, { usage: keyUsage }),
5434
+ /* @__PURE__ */ jsxs14(Box14, { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 1, mb: 1, children: [
5435
+ sectionLabel("Keys by member"),
5436
+ /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", noWrap: true, children: "Filter member keys, owner or alias" })
5437
+ ] }),
5438
+ keyUsage && /* @__PURE__ */ jsx15(TeamKeyUsageTable, { usage: keyUsage, filterText: searchValue }),
5377
5439
  !keyUsage && keyUsageFailed && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", children: "Could not load per-key usage" }),
5378
5440
  !keyUsage && !keyUsageFailed && /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5379
5441
  ] }),
5380
- /* @__PURE__ */ jsxs14(Box14, { children: [
5442
+ !memberUsage && /* @__PURE__ */ jsxs14(Box14, { children: [
5381
5443
  sectionLabel("Members"),
5382
5444
  team.members_with_roles?.length ? /* @__PURE__ */ jsx15(
5383
5445
  TableContainer4,
@@ -5535,8 +5597,8 @@ import MenuItem5 from "@mui/material/MenuItem";
5535
5597
  import Select from "@mui/material/Select";
5536
5598
  import FormControl from "@mui/material/FormControl";
5537
5599
  import InputLabel from "@mui/material/InputLabel";
5538
- import TextField5 from "@mui/material/TextField";
5539
- import InputAdornment3 from "@mui/material/InputAdornment";
5600
+ import TextField6 from "@mui/material/TextField";
5601
+ import InputAdornment4 from "@mui/material/InputAdornment";
5540
5602
  import { Search as Search2 } from "@mui/icons-material";
5541
5603
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
5542
5604
  import Tooltip7 from "@mui/material/Tooltip";
@@ -5675,7 +5737,7 @@ var init_ModelsTable = __esm({
5675
5737
  )
5676
5738
  ] }),
5677
5739
  /* @__PURE__ */ jsx16(
5678
- TextField5,
5740
+ TextField6,
5679
5741
  {
5680
5742
  label: "Filter Models",
5681
5743
  size: "small",
@@ -5684,7 +5746,7 @@ var init_ModelsTable = __esm({
5684
5746
  disabled: !selectedTeamId,
5685
5747
  sx: { minWidth: 240, mb: 2 },
5686
5748
  InputProps: {
5687
- startAdornment: /* @__PURE__ */ jsx16(InputAdornment3, { position: "start", children: /* @__PURE__ */ jsx16(Search2, { fontSize: "small" }) })
5749
+ startAdornment: /* @__PURE__ */ jsx16(InputAdornment4, { position: "start", children: /* @__PURE__ */ jsx16(Search2, { fontSize: "small" }) })
5688
5750
  }
5689
5751
  }
5690
5752
  )