@acarmisc/backstage-plugin-litellm 0.33.0 → 0.35.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.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { litellmPlugin } from './plugin';
1
+ export { litellmPlugin, litellmPlugin as default } from './plugin';
2
2
  export { LiteLLMPage } from './components/LiteLLMPage';
3
3
  export { DashboardHeader } from './components/DashboardHeader';
4
4
  export { KeysTable } from './components/KeysTable';
@@ -14,9 +14,8 @@ export { KeyFormDialog } from './components/KeyFormDialog';
14
14
  export type { KeyFormDialogMode, KeyFormDialogProps } from './components/KeyFormDialog';
15
15
  export { GenerateKeyButton } from './components/GenerateKeyButton';
16
16
  export type { GenerateKeyButtonProps } from './components/GenerateKeyButton';
17
- export { GenerateKeyDialog } from './components/GenerateKeyDialog';
18
17
  export { ManageTeamDialog } from './components/ManageTeamDialog';
19
18
  export { LiteLlmApi, liteLlmApiRef } from './api';
20
- export { litellmTeamCreatePermission, litellmTeamManagePermission, litellmTeamMembersManagePermission, litellmTeamKnowledgebaseManagePermission, litellmTeamMcpManagePermission, } from './permissions';
19
+ export { litellmTeamCreatePermission, litellmTeamManagePermission, litellmTeamMembersManagePermission, litellmTeamKnowledgebaseManagePermission, litellmTeamMcpManagePermission, } from '@acarmisc/backstage-plugin-litellm-common';
21
20
  export type { LiteLlmApiInterface } from './api';
22
21
  export * from './types';
package/dist/index.esm.js CHANGED
@@ -285,6 +285,12 @@ var init_api = __esm({
285
285
  end_date: endDate
286
286
  });
287
287
  }
288
+ async getTeamMemberUsage(teamId, startDate, endDate) {
289
+ return this.get(`/teams/${encodeURIComponent(teamId)}/usage/members`, {
290
+ start_date: startDate,
291
+ end_date: endDate
292
+ });
293
+ }
288
294
  async getConfig() {
289
295
  return this.get("/config");
290
296
  }
@@ -1823,8 +1829,6 @@ var init_KeysTable = __esm({
1823
1829
  setPruneSubmitting(true);
1824
1830
  try {
1825
1831
  await onPruneExpiredKeys();
1826
- } catch (error) {
1827
- console.error("Failed to prune expired keys:", error);
1828
1832
  } finally {
1829
1833
  setPruneSubmitting(false);
1830
1834
  setPruneConfirmCount(null);
@@ -1893,7 +1897,8 @@ var init_KeysTable = __esm({
1893
1897
  bgcolor: alpha7(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.08 : 0.05),
1894
1898
  px: 0.75,
1895
1899
  py: 0.375,
1896
- borderRadius: "5px"
1900
+ borderRadius: "5px",
1901
+ whiteSpace: "nowrap"
1897
1902
  }),
1898
1903
  children: shortKeyId(keyId)
1899
1904
  }
@@ -5011,13 +5016,49 @@ function budgetStatValue(hidden, budget) {
5011
5016
  if (budget > 0) return fmtUsd2(budget);
5012
5017
  return "Unlimited";
5013
5018
  }
5014
- var fmtUsd2, TeamCard, TeamUsage;
5019
+ var fmtUsd2, monoSx, MemberUsageTable, TeamCard, TeamUsage;
5015
5020
  var init_TeamUsage = __esm({
5016
5021
  "src/components/TeamUsage.tsx"() {
5017
5022
  "use strict";
5018
5023
  init_ui();
5019
5024
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
5020
- TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
5025
+ monoSx = { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 };
5026
+ MemberUsageTable = ({ usage }) => {
5027
+ const hidden = usage.budget_hidden === true;
5028
+ if (!usage.members.length) {
5029
+ return /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "No members or activity in this period." });
5030
+ }
5031
+ 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: [
5032
+ /* @__PURE__ */ jsx15(TableHead4, { children: /* @__PURE__ */ jsxs14(TableRow4, { children: [
5033
+ /* @__PURE__ */ jsx15(TableCell4, { children: "Member" }),
5034
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Spend" }),
5035
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Share" }),
5036
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Tokens" }),
5037
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Requests" }),
5038
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Success" }),
5039
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Keys" })
5040
+ ] }) }),
5041
+ /* @__PURE__ */ jsx15(TableBody4, { children: usage.members.map((m) => {
5042
+ const name = m.display_name ?? m.user_email ?? m.user_id;
5043
+ let sub = null;
5044
+ if (m.display_name) sub = m.user_email ?? m.user_id;
5045
+ else if (m.user_email && m.user_id !== m.user_email) sub = m.user_id;
5046
+ return /* @__PURE__ */ jsxs14(TableRow4, { children: [
5047
+ /* @__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: [
5048
+ /* @__PURE__ */ jsx15(Typography12, { variant: "body2", children: name }),
5049
+ sub && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", sx: monoSx, children: sub })
5050
+ ] }) }),
5051
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtUsd2(m.spend) }),
5052
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: `${m.spend_share_pct.toFixed(m.spend_share_pct > 0 && m.spend_share_pct < 1 ? 1 : 0)}%` }),
5053
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtCompact(m.total_tokens) }),
5054
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.api_requests.toLocaleString() }),
5055
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.success_rate === null ? "\u2014" : `${m.success_rate.toFixed(0)}%` }),
5056
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: m.user_id === null ? "\u2014" : m.key_count })
5057
+ ] }, m.user_id ?? "__unattributed");
5058
+ }) })
5059
+ ] }) });
5060
+ };
5061
+ TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
5021
5062
  const [expanded, setExpanded] = useState9(false);
5022
5063
  const chart = useChartTheme();
5023
5064
  const handleExpandChange = (newExpanded) => {
@@ -5210,6 +5251,10 @@ var init_TeamUsage = __esm({
5210
5251
  team.models?.length ? /* @__PURE__ */ jsx15(Box14, { display: "flex", gap: 0.5, flexWrap: "wrap", children: team.models.map((m) => /* @__PURE__ */ jsx15(TagChip, { label: m, title: m }, m)) }) : /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "All models allowed" })
5211
5252
  ] }),
5212
5253
  renderDailySpendSection(),
5254
+ (memberUsageLoading || memberUsage) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5255
+ sectionLabel("Usage by member"),
5256
+ memberUsage ? /* @__PURE__ */ jsx15(MemberUsageTable, { usage: memberUsage }) : /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5257
+ ] }),
5213
5258
  /* @__PURE__ */ jsxs14(Box14, { children: [
5214
5259
  sectionLabel("Members"),
5215
5260
  team.members_with_roles?.length ? /* @__PURE__ */ jsx15(
@@ -5264,6 +5309,8 @@ var init_TeamUsage = __esm({
5264
5309
  loading,
5265
5310
  getTeamUsage,
5266
5311
  getTeamUsageLoading,
5312
+ getTeamMemberUsage,
5313
+ getTeamMemberUsageLoading,
5267
5314
  canManage,
5268
5315
  onEditTeam,
5269
5316
  canCreate,
@@ -5323,6 +5370,8 @@ var init_TeamUsage = __esm({
5323
5370
  team,
5324
5371
  usage: getTeamUsage(team.team_id),
5325
5372
  usageLoading: getTeamUsageLoading(team.team_id),
5373
+ memberUsage: getTeamMemberUsage?.(team.team_id) ?? null,
5374
+ memberUsageLoading: getTeamMemberUsageLoading?.(team.team_id) ?? false,
5326
5375
  canManage,
5327
5376
  onEditTeam,
5328
5377
  canManageMembers: canManageMembers?.(team),
@@ -5900,28 +5949,6 @@ var init_dates = __esm({
5900
5949
  }
5901
5950
  });
5902
5951
 
5903
- // src/permissions.ts
5904
- import {
5905
- litellmKeyCreatePermission,
5906
- litellmKeyRevokePermission as litellmKeyRevokePermission2,
5907
- litellmKeyManagePermission as litellmKeyManagePermission2,
5908
- litellmKeyResetSpendPermission as litellmKeyResetSpendPermission2,
5909
- litellmKeyUnblockPermission as litellmKeyUnblockPermission2,
5910
- litellmAuditReadPermission,
5911
- litellmTeamCreatePermission,
5912
- litellmTeamManagePermission,
5913
- litellmTeamMembersManagePermission,
5914
- litellmTeamKnowledgebaseManagePermission,
5915
- litellmTeamMcpManagePermission,
5916
- litellmTeamDeletePermission,
5917
- litellmPermissions
5918
- } from "@acarmisc/backstage-plugin-litellm-common";
5919
- var init_permissions = __esm({
5920
- "src/permissions.ts"() {
5921
- "use strict";
5922
- }
5923
- });
5924
-
5925
5952
  // src/feedback.ts
5926
5953
  function toastFor(kind, errorMsg) {
5927
5954
  switch (kind) {
@@ -6033,6 +6060,13 @@ import { useAsync as useAsync2, useAsyncRetry } from "react-use";
6033
6060
  import { useApi as useApi4, alertApiRef as alertApiRef2 } from "@backstage/core-plugin-api";
6034
6061
  import { Content, Progress } from "@backstage/core-components";
6035
6062
  import { usePermission as usePermission3 } from "@backstage/plugin-permission-react";
6063
+ import {
6064
+ litellmTeamCreatePermission,
6065
+ litellmTeamManagePermission,
6066
+ litellmTeamMembersManagePermission,
6067
+ litellmTeamKnowledgebaseManagePermission,
6068
+ litellmTeamMcpManagePermission
6069
+ } from "@acarmisc/backstage-plugin-litellm-common";
6036
6070
  import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
6037
6071
  function initDateRange() {
6038
6072
  let preset = "7d";
@@ -6063,7 +6097,6 @@ var init_LiteLLMPage = __esm({
6063
6097
  init_AuditLog();
6064
6098
  init_api();
6065
6099
  init_dates();
6066
- init_permissions();
6067
6100
  init_feedback();
6068
6101
  init_teamMemberManager();
6069
6102
  init_unprovisionedCopy();
@@ -6151,6 +6184,8 @@ var init_LiteLLMPage = __esm({
6151
6184
  const keyFormOpen = generateDialogOpen || !!keyToEdit;
6152
6185
  const [teamUsageCache, setTeamUsageCache] = useState12({});
6153
6186
  const [teamUsageLoading, setTeamUsageLoading] = useState12({});
6187
+ const [memberUsageCache, setMemberUsageCache] = useState12({});
6188
+ const [memberUsageLoading, setMemberUsageLoading] = useState12({});
6154
6189
  const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
6155
6190
  () => api.getUserInfo(),
6156
6191
  [api]
@@ -6211,6 +6246,8 @@ var init_LiteLLMPage = __esm({
6211
6246
  }
6212
6247
  setTeamUsageCache({});
6213
6248
  setTeamUsageLoading({});
6249
+ setMemberUsageCache({});
6250
+ setMemberUsageLoading({});
6214
6251
  }, [setDateRange, setCurrentPreset]);
6215
6252
  const loadTeamUsage = useCallback4(async (teamId) => {
6216
6253
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
@@ -6226,6 +6263,20 @@ var init_LiteLLMPage = __esm({
6226
6263
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6227
6264
  }
6228
6265
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
6266
+ const loadMemberUsage = useCallback4(async (teamId) => {
6267
+ if (memberUsageCache[teamId] !== void 0 || memberUsageLoading[teamId]) return;
6268
+ setMemberUsageLoading((prev) => ({ ...prev, [teamId]: true }));
6269
+ try {
6270
+ const startDate = toLocalDay(dateRange.start);
6271
+ const endDate = toLocalDay(dateRange.end);
6272
+ const data = await api.getTeamMemberUsage(teamId, startDate, endDate);
6273
+ setMemberUsageCache((prev) => ({ ...prev, [teamId]: data }));
6274
+ } catch {
6275
+ setMemberUsageCache((prev) => ({ ...prev, [teamId]: null }));
6276
+ } finally {
6277
+ setMemberUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6278
+ }
6279
+ }, [api, dateRange, memberUsageCache, memberUsageLoading]);
6229
6280
  const allowedModels = useMemo9(() => {
6230
6281
  if (!allModels?.length) return [];
6231
6282
  const userModels = userInfo?.models;
@@ -6520,6 +6571,8 @@ var init_LiteLLMPage = __esm({
6520
6571
  loading: teamsLoading,
6521
6572
  getTeamUsage: (teamId) => teamUsageCache[teamId] ?? null,
6522
6573
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
6574
+ getTeamMemberUsage: (teamId) => memberUsageCache[teamId] ?? null,
6575
+ getTeamMemberUsageLoading: (teamId) => memberUsageLoading[teamId] ?? false,
6523
6576
  canManage: teamMgmtEnabled && canManageTeam && !readOnly,
6524
6577
  onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
6525
6578
  canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
@@ -6527,7 +6580,10 @@ var init_LiteLLMPage = __esm({
6527
6580
  canManageMembers: (t) => teamMgmtEnabled && canManageMembers && isTeamMemberManager(t, userInfo?.user_id, memberManagerRoles, readOnly),
6528
6581
  onManageMembers: (t) => setManageTeam({ mode: "members", team: t }),
6529
6582
  readOnly,
6530
- onTeamExpand: (team) => loadTeamUsage(team.team_id)
6583
+ onTeamExpand: (team) => {
6584
+ loadTeamUsage(team.team_id);
6585
+ if (liteLlmConfig?.teamUsage?.memberBreakdown) loadMemberUsage(team.team_id);
6586
+ }
6531
6587
  }
6532
6588
  );
6533
6589
  })(),
@@ -7616,36 +7672,18 @@ var LiteLLMBudgetGauges = ({
7616
7672
  // src/index.ts
7617
7673
  init_KeyFormDialog();
7618
7674
  init_GenerateKeyButton();
7619
-
7620
- // src/components/GenerateKeyDialog.tsx
7621
- init_KeyFormDialog();
7622
- import { jsx as jsx22 } from "react/jsx-runtime";
7623
- var GenerateKeyDialog = (props) => /* @__PURE__ */ jsx22(
7624
- KeyFormDialog,
7625
- {
7626
- open: props.open,
7627
- onClose: props.onClose,
7628
- mode: "create",
7629
- keys: props.keys,
7630
- models: props.models,
7631
- teams: props.teams,
7632
- username: props.username,
7633
- keyGenerationSettings: props.keyGenerationSettings,
7634
- onCreateKey: props.onGenerateKey,
7635
- onUpdateKey: async () => {
7636
- },
7637
- onGetConfig: props.onGetConfig
7638
- }
7639
- );
7640
-
7641
- // src/index.ts
7642
7675
  init_ManageTeamDialog();
7643
7676
  init_api();
7644
- init_permissions();
7677
+ import {
7678
+ litellmTeamCreatePermission as litellmTeamCreatePermission2,
7679
+ litellmTeamManagePermission as litellmTeamManagePermission2,
7680
+ litellmTeamMembersManagePermission as litellmTeamMembersManagePermission2,
7681
+ litellmTeamKnowledgebaseManagePermission as litellmTeamKnowledgebaseManagePermission2,
7682
+ litellmTeamMcpManagePermission as litellmTeamMcpManagePermission2
7683
+ } from "@acarmisc/backstage-plugin-litellm-common";
7645
7684
  export {
7646
7685
  DashboardHeader,
7647
7686
  GenerateKeyButton,
7648
- GenerateKeyDialog,
7649
7687
  KeyFormDialog,
7650
7688
  KeysTable,
7651
7689
  LiteLLMBudgetGauges,
@@ -7656,12 +7694,13 @@ export {
7656
7694
  ManageTeamDialog,
7657
7695
  TeamUsage,
7658
7696
  UsageStats,
7697
+ litellmPlugin as default,
7659
7698
  liteLlmApiRef,
7660
7699
  litellmPlugin,
7661
- litellmTeamCreatePermission,
7662
- litellmTeamKnowledgebaseManagePermission,
7663
- litellmTeamManagePermission,
7664
- litellmTeamMcpManagePermission,
7665
- litellmTeamMembersManagePermission
7700
+ litellmTeamCreatePermission2 as litellmTeamCreatePermission,
7701
+ litellmTeamKnowledgebaseManagePermission2 as litellmTeamKnowledgebaseManagePermission,
7702
+ litellmTeamManagePermission2 as litellmTeamManagePermission,
7703
+ litellmTeamMcpManagePermission2 as litellmTeamMcpManagePermission,
7704
+ litellmTeamMembersManagePermission2 as litellmTeamMembersManagePermission
7666
7705
  };
7667
7706
  //# sourceMappingURL=index.esm.js.map