@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/api.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type FetchApi, type DiscoveryApi } from '@backstage/frontend-plugin-api';
2
- import { UserInfo, VirtualKey, ModelInfo, UsageMetrics, TeamMemberUsage, TeamInfo, GenerateKeyRequest, GenerateKeyResponse, UpdateKeyRequest, AuditLogsParams, PaginatedAuditLogs, SpendLogsParams, PaginatedSpendLogs, LiteLlmConfig, QualityResponse, VectorStoreInfo, McpServerInfo, CreateTeamRequest, CreateTeamResponse, UpdateTeamRequest } from './types';
2
+ import { UserInfo, VirtualKey, ModelInfo, UsageMetrics, TeamMemberUsage, TeamKeyUsage, TeamInfo, GenerateKeyRequest, GenerateKeyResponse, UpdateKeyRequest, AuditLogsParams, PaginatedAuditLogs, SpendLogsParams, PaginatedSpendLogs, LiteLlmConfig, QualityResponse, VectorStoreInfo, McpServerInfo, CreateTeamRequest, CreateTeamResponse, UpdateTeamRequest } from './types';
3
3
  export declare class ApiError extends Error {
4
4
  body: unknown;
5
5
  status: number;
@@ -49,6 +49,8 @@ export interface LiteLlmApiInterface {
49
49
  getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics>;
50
50
  /** Per-member breakdown; rejects (403/404) when the caller may not see it. */
51
51
  getTeamMemberUsage(teamId: string, startDate: string, endDate: string): Promise<TeamMemberUsage>;
52
+ /** A team's keys with per-key usage; same access rules as the per-member breakdown. */
53
+ getTeamKeyUsage(teamId: string, startDate: string, endDate: string): Promise<TeamKeyUsage>;
52
54
  getAuditLogs(params: AuditLogsParams): Promise<PaginatedAuditLogs>;
53
55
  getSpendLogs(params: SpendLogsParams): Promise<PaginatedSpendLogs>;
54
56
  /** Langfuse quality summary; `{configured:false}` when Langfuse is off. */
@@ -120,6 +122,7 @@ export declare class LiteLlmApi implements LiteLlmApiInterface {
120
122
  getUsage(startDate: string, endDate: string): Promise<UsageMetrics>;
121
123
  getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics>;
122
124
  getTeamMemberUsage(teamId: string, startDate: string, endDate: string): Promise<TeamMemberUsage>;
125
+ getTeamKeyUsage(teamId: string, startDate: string, endDate: string): Promise<TeamKeyUsage>;
123
126
  getSpendLogs(params: SpendLogsParams): Promise<PaginatedSpendLogs>;
124
127
  getQuality(params: SpendLogsParams): Promise<QualityResponse>;
125
128
  getConfig(): Promise<LiteLlmConfig>;
@@ -0,0 +1,21 @@
1
+ import { FC } from 'react';
2
+ import { GenerateKeyRequest, GenerateKeyResponse, LiteLlmConfig, ModelInfo, TeamInfo, VirtualKey } from '../types';
3
+ /**
4
+ * @deprecated Use {@link KeyFormDialog} with `mode="create"` instead — this
5
+ * wrapper only forwards props for backwards compatibility with the public
6
+ * entrypoint and will be removed in a future release.
7
+ */
8
+ export declare const GenerateKeyDialog: FC<{
9
+ open: boolean;
10
+ onClose: () => void;
11
+ keys: VirtualKey[];
12
+ models: ModelInfo[];
13
+ teams: TeamInfo[];
14
+ username?: string;
15
+ keyGenerationSettings?: {
16
+ allowUnlimitedBudget: boolean;
17
+ teamRequired: boolean;
18
+ };
19
+ onGenerateKey: (request: GenerateKeyRequest) => Promise<GenerateKeyResponse>;
20
+ onGetConfig: () => Promise<LiteLlmConfig>;
21
+ }>;
@@ -1,5 +1,5 @@
1
1
  import { FC } from 'react';
2
- import { McpServerInfo, TeamInfo, TeamMemberUsage, UsageMetrics, VectorStoreInfo } from '../types';
2
+ import { McpServerInfo, TeamInfo, TeamKeyUsage, TeamMemberUsage, UsageMetrics, VectorStoreInfo } from '../types';
3
3
  /**
4
4
  * Per-member usage for the period, sorted by spend. Dollars are replaced by
5
5
  * the share of team spend when the backend hides them.
@@ -7,6 +7,13 @@ import { McpServerInfo, TeamInfo, TeamMemberUsage, UsageMetrics, VectorStoreInfo
7
7
  export declare const MemberUsageTable: FC<{
8
8
  usage: TeamMemberUsage;
9
9
  }>;
10
+ /**
11
+ * A team's keys with the period's usage, for team leads. The backend never
12
+ * sends secrets; dollars are dropped when it hides them.
13
+ */
14
+ export declare const TeamKeyUsageTable: FC<{
15
+ usage: TeamKeyUsage;
16
+ }>;
10
17
  interface TeamUsageProps {
11
18
  teams: TeamInfo[];
12
19
  loading: boolean;
@@ -15,6 +22,12 @@ interface TeamUsageProps {
15
22
  /** Per-member breakdown for a team; null hides the section. */
16
23
  getTeamMemberUsage?: (teamId: string) => TeamMemberUsage | null;
17
24
  getTeamMemberUsageLoading?: (teamId: string) => boolean;
25
+ /** Per-key usage for a team; null hides the section. */
26
+ getTeamKeyUsage?: (teamId: string) => TeamKeyUsage | null;
27
+ getTeamKeyUsageLoading?: (teamId: string) => boolean;
28
+ /** True when the breakdown failed for a reason other than refusal: shows a caption. */
29
+ getTeamMemberUsageFailed?: (teamId: string) => boolean;
30
+ getTeamKeyUsageFailed?: (teamId: string) => boolean;
18
31
  canManage?: boolean;
19
32
  onEditTeam?: (team: TeamInfo) => void;
20
33
  /** When true (with `onCreateTeam`), a "Create Team" action renders in the section header. */
package/dist/index.cjs.js CHANGED
@@ -321,6 +321,12 @@ var init_api = __esm({
321
321
  end_date: endDate
322
322
  });
323
323
  }
324
+ async getTeamKeyUsage(teamId, startDate, endDate) {
325
+ return this.get(`/teams/${encodeURIComponent(teamId)}/usage/keys`, {
326
+ start_date: startDate,
327
+ end_date: endDate
328
+ });
329
+ }
324
330
  async getSpendLogs(params) {
325
331
  const strParams = {
326
332
  start_date: params.startDate,
@@ -5054,7 +5060,7 @@ function budgetStatValue(hidden, budget) {
5054
5060
  if (budget > 0) return fmtUsd2(budget);
5055
5061
  return "Unlimited";
5056
5062
  }
5057
- 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, MemberUsageTable, 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_CircularProgress4, import_styles10, import_Tooltip5, import_icons_material7, import_recharts2, import_jsx_runtime15, fmtUsd2, monoSx, MemberUsageTable, TeamKeyUsageTable, TeamCard, TeamUsage;
5058
5064
  var init_TeamUsage = __esm({
5059
5065
  "src/components/TeamUsage.tsx"() {
5060
5066
  "use strict";
@@ -5117,7 +5123,47 @@ var init_TeamUsage = __esm({
5117
5123
  }) })
5118
5124
  ] }) });
5119
5125
  };
5120
- TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5126
+ TeamKeyUsageTable = ({ usage }) => {
5127
+ const hidden = usage.budget_hidden === true;
5128
+ if (!usage.keys.length) {
5129
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "This team has no keys." });
5130
+ }
5131
+ 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: [
5132
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableHead4.default, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5133
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Key" }),
5134
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Owner" }),
5135
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Created" }),
5136
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Status" }),
5137
+ !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Spend" }),
5138
+ !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Budget" }),
5139
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Tokens" }),
5140
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Requests" })
5141
+ ] }) }),
5142
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableBody4.default, { children: usage.keys.map((k) => {
5143
+ const owner = k.display_name ?? k.user_email ?? k.user_id;
5144
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5145
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableCell4.default, { children: [
5146
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", children: k.key_alias ?? k.key }),
5147
+ k.key_alias && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", sx: monoSx, children: k.key })
5148
+ ] }),
5149
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: owner ?? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "\u2014" }) }),
5150
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: fmtDateShort(k.created_at) }),
5151
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5152
+ StatusPill,
5153
+ {
5154
+ label: k.blocked ? "Blocked" : "Active",
5155
+ tone: k.blocked ? "danger" : "success"
5156
+ }
5157
+ ) }),
5158
+ !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: fmtUsd2(k.spend) }),
5159
+ !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: k.max_budget === void 0 ? "Unlimited" : fmtUsd2(k.max_budget) }),
5160
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: fmtCompact(k.total_tokens) }),
5161
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: k.api_requests.toLocaleString() })
5162
+ ] }, k.key);
5163
+ }) })
5164
+ ] }) });
5165
+ };
5166
+ TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, memberUsageFailed, keyUsage, keyUsageLoading, keyUsageFailed, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5121
5167
  const [expanded, setExpanded] = (0, import_react10.useState)(false);
5122
5168
  const chart = useChartTheme();
5123
5169
  const handleExpandChange = (newExpanded) => {
@@ -5316,9 +5362,17 @@ var init_TeamUsage = __esm({
5316
5362
  renderNamedTags("Knowledge bases", team.object_permission?.vector_stores, vectorStores),
5317
5363
  renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5318
5364
  renderDailySpendSection(),
5319
- (memberUsageLoading || memberUsage) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5365
+ (memberUsageLoading || memberUsage || memberUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5320
5366
  sectionLabel("Usage by member"),
5321
- memberUsage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MemberUsageTable, { usage: memberUsage }) : /* @__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 }) })
5367
+ memberUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MemberUsageTable, { usage: memberUsage }),
5368
+ !memberUsage && memberUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-member usage" }),
5369
+ !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 }) })
5370
+ ] }),
5371
+ (keyUsageLoading || keyUsage || keyUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5372
+ sectionLabel("Keys by member"),
5373
+ keyUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TeamKeyUsageTable, { usage: keyUsage }),
5374
+ !keyUsage && keyUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-key usage" }),
5375
+ !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 }) })
5322
5376
  ] }),
5323
5377
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5324
5378
  sectionLabel("Members"),
@@ -5376,6 +5430,10 @@ var init_TeamUsage = __esm({
5376
5430
  getTeamUsageLoading,
5377
5431
  getTeamMemberUsage,
5378
5432
  getTeamMemberUsageLoading,
5433
+ getTeamKeyUsage,
5434
+ getTeamKeyUsageLoading,
5435
+ getTeamMemberUsageFailed,
5436
+ getTeamKeyUsageFailed,
5379
5437
  canManage,
5380
5438
  onEditTeam,
5381
5439
  canCreate,
@@ -5439,6 +5497,10 @@ var init_TeamUsage = __esm({
5439
5497
  usageLoading: getTeamUsageLoading(team.team_id),
5440
5498
  memberUsage: getTeamMemberUsage?.(team.team_id) ?? null,
5441
5499
  memberUsageLoading: getTeamMemberUsageLoading?.(team.team_id) ?? false,
5500
+ keyUsage: getTeamKeyUsage?.(team.team_id) ?? null,
5501
+ keyUsageLoading: getTeamKeyUsageLoading?.(team.team_id) ?? false,
5502
+ memberUsageFailed: getTeamMemberUsageFailed?.(team.team_id) ?? false,
5503
+ keyUsageFailed: getTeamKeyUsageFailed?.(team.team_id) ?? false,
5442
5504
  canManage,
5443
5505
  onEditTeam,
5444
5506
  canManageMembers: canManageMembers?.(team),
@@ -6262,7 +6324,7 @@ function initDateRange() {
6262
6324
  else start.setDate(start.getDate() - 7);
6263
6325
  return { start, end };
6264
6326
  }
6265
- var import_react13, import_Box17, import_Alert6, import_Button8, import_Typography15, import_Tabs2, import_Tab2, import_Grid3, import_Accordion2, import_AccordionSummary2, import_AccordionDetails2, import_ExpandMore, import_react_router_dom, import_react_use2, import_core_plugin_api5, import_core_components3, import_plugin_permission_react3, import_backstage_plugin_litellm_common5, import_jsx_runtime18, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
6327
+ var import_react13, import_Box17, import_Alert6, import_Button8, import_Typography15, import_Tabs2, import_Tab2, import_Grid3, import_Accordion2, import_AccordionSummary2, import_AccordionDetails2, import_ExpandMore, import_react_router_dom, import_react_use2, import_core_plugin_api5, import_core_components3, import_plugin_permission_react3, import_backstage_plugin_litellm_common5, import_jsx_runtime18, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, isRefusal, LiteLLMPage;
6266
6328
  var init_LiteLLMPage = __esm({
6267
6329
  "src/components/LiteLLMPage.tsx"() {
6268
6330
  "use strict";
@@ -6302,6 +6364,7 @@ var init_LiteLLMPage = __esm({
6302
6364
  PERIOD_LS_KEY2 = "litellm_usage_period";
6303
6365
  PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
6304
6366
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
6367
+ isRefusal = (err) => err instanceof ApiError && (err.status === 403 || err.status === 404);
6305
6368
  LiteLLMPage = () => {
6306
6369
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
6307
6370
  const alertApi = (0, import_core_plugin_api5.useApi)(import_core_plugin_api5.alertApiRef);
@@ -6385,6 +6448,10 @@ var init_LiteLLMPage = __esm({
6385
6448
  const [teamUsageLoading, setTeamUsageLoading] = (0, import_react13.useState)({});
6386
6449
  const [memberUsageCache, setMemberUsageCache] = (0, import_react13.useState)({});
6387
6450
  const [memberUsageLoading, setMemberUsageLoading] = (0, import_react13.useState)({});
6451
+ const [keyUsageCache, setKeyUsageCache] = (0, import_react13.useState)({});
6452
+ const [keyUsageLoading, setKeyUsageLoading] = (0, import_react13.useState)({});
6453
+ const [memberUsageFailed, setMemberUsageFailed] = (0, import_react13.useState)({});
6454
+ const [keyUsageFailed, setKeyUsageFailed] = (0, import_react13.useState)({});
6388
6455
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use2.useAsync)(
6389
6456
  () => api.getUserInfo(),
6390
6457
  [api]
@@ -6451,6 +6518,10 @@ var init_LiteLLMPage = __esm({
6451
6518
  setTeamUsageLoading({});
6452
6519
  setMemberUsageCache({});
6453
6520
  setMemberUsageLoading({});
6521
+ setKeyUsageCache({});
6522
+ setKeyUsageLoading({});
6523
+ setMemberUsageFailed({});
6524
+ setKeyUsageFailed({});
6454
6525
  }, [setDateRange, setCurrentPreset]);
6455
6526
  const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
6456
6527
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
@@ -6474,12 +6545,28 @@ var init_LiteLLMPage = __esm({
6474
6545
  const endDate = toLocalDay(dateRange.end);
6475
6546
  const data = await api.getTeamMemberUsage(teamId, startDate, endDate);
6476
6547
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: data }));
6477
- } catch {
6548
+ } catch (err) {
6478
6549
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: null }));
6550
+ setMemberUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6479
6551
  } finally {
6480
6552
  setMemberUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6481
6553
  }
6482
6554
  }, [api, dateRange, memberUsageCache, memberUsageLoading]);
6555
+ const loadKeyUsage = (0, import_react13.useCallback)(async (teamId) => {
6556
+ if (keyUsageCache[teamId] !== void 0 || keyUsageLoading[teamId]) return;
6557
+ setKeyUsageLoading((prev) => ({ ...prev, [teamId]: true }));
6558
+ try {
6559
+ const startDate = toLocalDay(dateRange.start);
6560
+ const endDate = toLocalDay(dateRange.end);
6561
+ const data = await api.getTeamKeyUsage(teamId, startDate, endDate);
6562
+ setKeyUsageCache((prev) => ({ ...prev, [teamId]: data }));
6563
+ } catch (err) {
6564
+ setKeyUsageCache((prev) => ({ ...prev, [teamId]: null }));
6565
+ setKeyUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6566
+ } finally {
6567
+ setKeyUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6568
+ }
6569
+ }, [api, dateRange, keyUsageCache, keyUsageLoading]);
6483
6570
  const allowedModels = (0, import_react13.useMemo)(() => {
6484
6571
  if (!allModels?.length) return [];
6485
6572
  const userModels = userInfo?.models;
@@ -6777,6 +6864,10 @@ var init_LiteLLMPage = __esm({
6777
6864
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
6778
6865
  getTeamMemberUsage: (teamId) => memberUsageCache[teamId] ?? null,
6779
6866
  getTeamMemberUsageLoading: (teamId) => memberUsageLoading[teamId] ?? false,
6867
+ getTeamKeyUsage: (teamId) => keyUsageCache[teamId] ?? null,
6868
+ getTeamKeyUsageLoading: (teamId) => keyUsageLoading[teamId] ?? false,
6869
+ getTeamMemberUsageFailed: (teamId) => memberUsageFailed[teamId] ?? false,
6870
+ getTeamKeyUsageFailed: (teamId) => keyUsageFailed[teamId] ?? false,
6780
6871
  canManage: teamMgmtEnabled && canManageTeam && !readOnly,
6781
6872
  onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
6782
6873
  canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
@@ -6788,7 +6879,10 @@ var init_LiteLLMPage = __esm({
6788
6879
  readOnly,
6789
6880
  onTeamExpand: (team) => {
6790
6881
  loadTeamUsage(team.team_id);
6791
- if (liteLlmConfig?.teamUsage?.memberBreakdown) loadMemberUsage(team.team_id);
6882
+ if (liteLlmConfig?.teamUsage?.memberBreakdown) {
6883
+ loadMemberUsage(team.team_id);
6884
+ loadKeyUsage(team.team_id);
6885
+ }
6792
6886
  }
6793
6887
  }
6794
6888
  );