@acarmisc/backstage-plugin-litellm 0.43.0 → 0.45.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
@@ -299,6 +299,12 @@ var init_api = __esm({
299
299
  end_date: endDate
300
300
  });
301
301
  }
302
+ async getTeamKeyUsage(teamId, startDate, endDate) {
303
+ return this.get(`/teams/${encodeURIComponent(teamId)}/usage/keys`, {
304
+ start_date: startDate,
305
+ end_date: endDate
306
+ });
307
+ }
302
308
  async getSpendLogs(params) {
303
309
  const strParams = {
304
310
  start_date: params.startDate,
@@ -5078,7 +5084,7 @@ function budgetStatValue(hidden, budget) {
5078
5084
  if (budget > 0) return fmtUsd2(budget);
5079
5085
  return "Unlimited";
5080
5086
  }
5081
- var fmtUsd2, monoSx, MemberUsageTable, TeamCard, TeamUsage;
5087
+ var fmtUsd2, monoSx, MemberUsageTable, TeamKeyUsageTable, TeamCard, TeamUsage;
5082
5088
  var init_TeamUsage = __esm({
5083
5089
  "src/components/TeamUsage.tsx"() {
5084
5090
  "use strict";
@@ -5120,7 +5126,47 @@ var init_TeamUsage = __esm({
5120
5126
  }) })
5121
5127
  ] }) });
5122
5128
  };
5123
- TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5129
+ TeamKeyUsageTable = ({ usage }) => {
5130
+ const hidden = usage.budget_hidden === true;
5131
+ if (!usage.keys.length) {
5132
+ return /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "This team has no keys." });
5133
+ }
5134
+ 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
+ /* @__PURE__ */ jsx15(TableHead4, { children: /* @__PURE__ */ jsxs14(TableRow4, { children: [
5136
+ /* @__PURE__ */ jsx15(TableCell4, { children: "Key" }),
5137
+ /* @__PURE__ */ jsx15(TableCell4, { children: "Owner" }),
5138
+ /* @__PURE__ */ jsx15(TableCell4, { children: "Created" }),
5139
+ /* @__PURE__ */ jsx15(TableCell4, { children: "Status" }),
5140
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Spend" }),
5141
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Budget" }),
5142
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Tokens" }),
5143
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: "Requests" })
5144
+ ] }) }),
5145
+ /* @__PURE__ */ jsx15(TableBody4, { children: usage.keys.map((k) => {
5146
+ const owner = k.display_name ?? k.user_email ?? k.user_id;
5147
+ return /* @__PURE__ */ jsxs14(TableRow4, { children: [
5148
+ /* @__PURE__ */ jsxs14(TableCell4, { children: [
5149
+ /* @__PURE__ */ jsx15(Typography12, { variant: "body2", children: k.key_alias ?? k.key }),
5150
+ k.key_alias && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", sx: monoSx, children: k.key })
5151
+ ] }),
5152
+ /* @__PURE__ */ jsx15(TableCell4, { children: owner ?? /* @__PURE__ */ jsx15(Typography12, { variant: "body2", color: "text.secondary", children: "\u2014" }) }),
5153
+ /* @__PURE__ */ jsx15(TableCell4, { children: fmtDateShort(k.created_at) }),
5154
+ /* @__PURE__ */ jsx15(TableCell4, { children: /* @__PURE__ */ jsx15(
5155
+ StatusPill,
5156
+ {
5157
+ label: k.blocked ? "Blocked" : "Active",
5158
+ tone: k.blocked ? "danger" : "success"
5159
+ }
5160
+ ) }),
5161
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtUsd2(k.spend) }),
5162
+ !hidden && /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: k.max_budget === void 0 ? "Unlimited" : fmtUsd2(k.max_budget) }),
5163
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: fmtCompact(k.total_tokens) }),
5164
+ /* @__PURE__ */ jsx15(TableCell4, { align: "right", children: k.api_requests.toLocaleString() })
5165
+ ] }, k.key);
5166
+ }) })
5167
+ ] }) });
5168
+ };
5169
+ TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, memberUsageFailed, keyUsage, keyUsageLoading, keyUsageFailed, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5124
5170
  const [expanded, setExpanded] = useState9(false);
5125
5171
  const chart = useChartTheme();
5126
5172
  const handleExpandChange = (newExpanded) => {
@@ -5319,9 +5365,17 @@ var init_TeamUsage = __esm({
5319
5365
  renderNamedTags("Knowledge bases", team.object_permission?.vector_stores, vectorStores),
5320
5366
  renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5321
5367
  renderDailySpendSection(),
5322
- (memberUsageLoading || memberUsage) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5368
+ (memberUsageLoading || memberUsage || memberUsageFailed) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5323
5369
  sectionLabel("Usage by member"),
5324
- memberUsage ? /* @__PURE__ */ jsx15(MemberUsageTable, { usage: memberUsage }) : /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5370
+ memberUsage && /* @__PURE__ */ jsx15(MemberUsageTable, { usage: memberUsage }),
5371
+ !memberUsage && memberUsageFailed && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", children: "Could not load per-member usage" }),
5372
+ !memberUsage && !memberUsageFailed && /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5373
+ ] }),
5374
+ (keyUsageLoading || keyUsage || keyUsageFailed) && /* @__PURE__ */ jsxs14(Box14, { mb: 2.5, children: [
5375
+ sectionLabel("Keys by member"),
5376
+ keyUsage && /* @__PURE__ */ jsx15(TeamKeyUsageTable, { usage: keyUsage }),
5377
+ !keyUsage && keyUsageFailed && /* @__PURE__ */ jsx15(Typography12, { variant: "caption", color: "text.secondary", children: "Could not load per-key usage" }),
5378
+ !keyUsage && !keyUsageFailed && /* @__PURE__ */ jsx15(Box14, { display: "flex", justifyContent: "center", py: 2, children: /* @__PURE__ */ jsx15(CircularProgress4, { size: 22 }) })
5325
5379
  ] }),
5326
5380
  /* @__PURE__ */ jsxs14(Box14, { children: [
5327
5381
  sectionLabel("Members"),
@@ -5379,6 +5433,10 @@ var init_TeamUsage = __esm({
5379
5433
  getTeamUsageLoading,
5380
5434
  getTeamMemberUsage,
5381
5435
  getTeamMemberUsageLoading,
5436
+ getTeamKeyUsage,
5437
+ getTeamKeyUsageLoading,
5438
+ getTeamMemberUsageFailed,
5439
+ getTeamKeyUsageFailed,
5382
5440
  canManage,
5383
5441
  onEditTeam,
5384
5442
  canCreate,
@@ -5442,6 +5500,10 @@ var init_TeamUsage = __esm({
5442
5500
  usageLoading: getTeamUsageLoading(team.team_id),
5443
5501
  memberUsage: getTeamMemberUsage?.(team.team_id) ?? null,
5444
5502
  memberUsageLoading: getTeamMemberUsageLoading?.(team.team_id) ?? false,
5503
+ keyUsage: getTeamKeyUsage?.(team.team_id) ?? null,
5504
+ keyUsageLoading: getTeamKeyUsageLoading?.(team.team_id) ?? false,
5505
+ memberUsageFailed: getTeamMemberUsageFailed?.(team.team_id) ?? false,
5506
+ keyUsageFailed: getTeamKeyUsageFailed?.(team.team_id) ?? false,
5445
5507
  canManage,
5446
5508
  onEditTeam,
5447
5509
  canManageMembers: canManageMembers?.(team),
@@ -6290,7 +6352,7 @@ function initDateRange() {
6290
6352
  else start.setDate(start.getDate() - 7);
6291
6353
  return { start, end };
6292
6354
  }
6293
- var PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
6355
+ var PERIOD_LS_KEY2, PAGE_TABS, isPageTab, isRefusal, LiteLLMPage;
6294
6356
  var init_LiteLLMPage = __esm({
6295
6357
  "src/components/LiteLLMPage.tsx"() {
6296
6358
  "use strict";
@@ -6311,6 +6373,7 @@ var init_LiteLLMPage = __esm({
6311
6373
  PERIOD_LS_KEY2 = "litellm_usage_period";
6312
6374
  PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
6313
6375
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
6376
+ isRefusal = (err) => err instanceof ApiError && (err.status === 403 || err.status === 404);
6314
6377
  LiteLLMPage = () => {
6315
6378
  const api = useApi4(liteLlmApiRef);
6316
6379
  const alertApi = useApi4(alertApiRef2);
@@ -6394,6 +6457,10 @@ var init_LiteLLMPage = __esm({
6394
6457
  const [teamUsageLoading, setTeamUsageLoading] = useState11({});
6395
6458
  const [memberUsageCache, setMemberUsageCache] = useState11({});
6396
6459
  const [memberUsageLoading, setMemberUsageLoading] = useState11({});
6460
+ const [keyUsageCache, setKeyUsageCache] = useState11({});
6461
+ const [keyUsageLoading, setKeyUsageLoading] = useState11({});
6462
+ const [memberUsageFailed, setMemberUsageFailed] = useState11({});
6463
+ const [keyUsageFailed, setKeyUsageFailed] = useState11({});
6397
6464
  const { value: userInfo, loading: userLoading, error: userError } = useAsync(
6398
6465
  () => api.getUserInfo(),
6399
6466
  [api]
@@ -6460,6 +6527,10 @@ var init_LiteLLMPage = __esm({
6460
6527
  setTeamUsageLoading({});
6461
6528
  setMemberUsageCache({});
6462
6529
  setMemberUsageLoading({});
6530
+ setKeyUsageCache({});
6531
+ setKeyUsageLoading({});
6532
+ setMemberUsageFailed({});
6533
+ setKeyUsageFailed({});
6463
6534
  }, [setDateRange, setCurrentPreset]);
6464
6535
  const loadTeamUsage = useCallback4(async (teamId) => {
6465
6536
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
@@ -6483,12 +6554,28 @@ var init_LiteLLMPage = __esm({
6483
6554
  const endDate = toLocalDay(dateRange.end);
6484
6555
  const data = await api.getTeamMemberUsage(teamId, startDate, endDate);
6485
6556
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: data }));
6486
- } catch {
6557
+ } catch (err) {
6487
6558
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: null }));
6559
+ setMemberUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6488
6560
  } finally {
6489
6561
  setMemberUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6490
6562
  }
6491
6563
  }, [api, dateRange, memberUsageCache, memberUsageLoading]);
6564
+ const loadKeyUsage = useCallback4(async (teamId) => {
6565
+ if (keyUsageCache[teamId] !== void 0 || keyUsageLoading[teamId]) return;
6566
+ setKeyUsageLoading((prev) => ({ ...prev, [teamId]: true }));
6567
+ try {
6568
+ const startDate = toLocalDay(dateRange.start);
6569
+ const endDate = toLocalDay(dateRange.end);
6570
+ const data = await api.getTeamKeyUsage(teamId, startDate, endDate);
6571
+ setKeyUsageCache((prev) => ({ ...prev, [teamId]: data }));
6572
+ } catch (err) {
6573
+ setKeyUsageCache((prev) => ({ ...prev, [teamId]: null }));
6574
+ setKeyUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6575
+ } finally {
6576
+ setKeyUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6577
+ }
6578
+ }, [api, dateRange, keyUsageCache, keyUsageLoading]);
6492
6579
  const allowedModels = useMemo9(() => {
6493
6580
  if (!allModels?.length) return [];
6494
6581
  const userModels = userInfo?.models;
@@ -6786,6 +6873,10 @@ var init_LiteLLMPage = __esm({
6786
6873
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
6787
6874
  getTeamMemberUsage: (teamId) => memberUsageCache[teamId] ?? null,
6788
6875
  getTeamMemberUsageLoading: (teamId) => memberUsageLoading[teamId] ?? false,
6876
+ getTeamKeyUsage: (teamId) => keyUsageCache[teamId] ?? null,
6877
+ getTeamKeyUsageLoading: (teamId) => keyUsageLoading[teamId] ?? false,
6878
+ getTeamMemberUsageFailed: (teamId) => memberUsageFailed[teamId] ?? false,
6879
+ getTeamKeyUsageFailed: (teamId) => keyUsageFailed[teamId] ?? false,
6789
6880
  canManage: teamMgmtEnabled && canManageTeam && !readOnly,
6790
6881
  onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
6791
6882
  canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
@@ -6797,7 +6888,10 @@ var init_LiteLLMPage = __esm({
6797
6888
  readOnly,
6798
6889
  onTeamExpand: (team) => {
6799
6890
  loadTeamUsage(team.team_id);
6800
- if (liteLlmConfig?.teamUsage?.memberBreakdown) loadMemberUsage(team.team_id);
6891
+ if (liteLlmConfig?.teamUsage?.memberBreakdown) {
6892
+ loadMemberUsage(team.team_id);
6893
+ loadKeyUsage(team.team_id);
6894
+ }
6801
6895
  }
6802
6896
  }
6803
6897
  );