@acarmisc/backstage-plugin-litellm 0.44.0 → 0.46.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.
@@ -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,10 +1,11 @@
1
1
  import { FC } from 'react';
2
2
  import { McpServerInfo, TeamInfo, TeamKeyUsage, TeamMemberUsage, UsageMetrics, VectorStoreInfo } from '../types';
3
3
  /**
4
- * Per-member usage for the period, sorted by spend. Dollars are replaced by
5
- * the share of team spend when the backend hides them.
4
+ * Per-member usage for the period as mini-cards, sorted by spend (the order
5
+ * the backend sends). Dollars are replaced by the share of team spend when the
6
+ * backend hides them.
6
7
  */
7
- export declare const MemberUsageTable: FC<{
8
+ export declare const MemberUsageCards: FC<{
8
9
  usage: TeamMemberUsage;
9
10
  }>;
10
11
  /**
@@ -25,6 +26,9 @@ interface TeamUsageProps {
25
26
  /** Per-key usage for a team; null hides the section. */
26
27
  getTeamKeyUsage?: (teamId: string) => TeamKeyUsage | null;
27
28
  getTeamKeyUsageLoading?: (teamId: string) => boolean;
29
+ /** True when the breakdown failed for a reason other than refusal: shows a caption. */
30
+ getTeamMemberUsageFailed?: (teamId: string) => boolean;
31
+ getTeamKeyUsageFailed?: (teamId: string) => boolean;
28
32
  canManage?: boolean;
29
33
  onEditTeam?: (team: TeamInfo) => void;
30
34
  /** When true (with `onCreateTeam`), a "Create Team" action renders in the section header. */
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, MemberUsageTable, 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_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";
@@ -5088,40 +5088,63 @@ var init_TeamUsage = __esm({
5088
5088
  import_jsx_runtime15 = require("react/jsx-runtime");
5089
5089
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
5090
5090
  monoSx = { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 };
5091
- MemberUsageTable = ({ usage }) => {
5091
+ fmtShare = (pct) => `${pct.toFixed(pct > 0 && pct < 1 ? 1 : 0)}%`;
5092
+ MemberUsageCards = ({ usage }) => {
5092
5093
  const hidden = usage.budget_hidden === true;
5093
5094
  if (!usage.members.length) {
5094
5095
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "No members or activity in this period." });
5095
5096
  }
5096
- 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": "Usage by member", children: [
5097
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableHead4.default, { children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5098
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: "Member" }),
5099
- !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Spend" }),
5100
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Share" }),
5101
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Tokens" }),
5102
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Requests" }),
5103
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Success" }),
5104
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: "Keys" })
5105
- ] }) }),
5106
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableBody4.default, { children: usage.members.map((m) => {
5107
- const name = m.display_name ?? m.user_email ?? m.user_id;
5108
- let sub = null;
5109
- if (m.display_name) sub = m.user_email ?? m.user_id;
5110
- else if (m.user_email && m.user_id !== m.user_email) sub = m.user_id;
5111
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_TableRow4.default, { children: [
5112
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { children: m.user_id === null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Tooltip5.default, { title: "Activity of keys that were deleted or have no owner", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: "Unattributed" }) }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5113
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", children: name }),
5114
- sub && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", sx: monoSx, children: sub })
5115
- ] }) }),
5116
- !hidden && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: fmtUsd2(m.spend) }),
5117
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: `${m.spend_share_pct.toFixed(m.spend_share_pct > 0 && m.spend_share_pct < 1 ? 1 : 0)}%` }),
5118
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: fmtCompact(m.total_tokens) }),
5119
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: m.api_requests.toLocaleString() }),
5120
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: m.success_rate === null ? "\u2014" : `${m.success_rate.toFixed(0)}%` }),
5121
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_TableCell4.default, { align: "right", children: m.user_id === null ? "\u2014" : m.key_count })
5122
- ] }, m.user_id ?? "__unattributed");
5123
- }) })
5124
- ] }) });
5097
+ const people = usage.members.filter((m) => m.user_id !== null).length;
5098
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5099
+ !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)(
5101
+ import_Box14.default,
5102
+ {
5103
+ role: "list",
5104
+ "aria-label": "Usage by member",
5105
+ sx: { display: "grid", gap: 1.5, gridTemplateColumns: "repeat(auto-fill, minmax(230px, 1fr))" },
5106
+ children: usage.members.map((m) => {
5107
+ const name = m.display_name ?? m.user_email ?? m.user_id;
5108
+ let sub = null;
5109
+ if (m.display_name) sub = m.user_email ?? m.user_id;
5110
+ else if (m.user_email && m.user_id !== m.user_email) sub = m.user_id;
5111
+ const stats = [
5112
+ ["Tokens", fmtCompact(m.total_tokens)],
5113
+ ["Requests", m.api_requests.toLocaleString()],
5114
+ ["Success", m.success_rate === null ? "\u2014" : `${m.success_rate.toFixed(0)}%`],
5115
+ ["Keys", m.user_id === null ? "\u2014" : String(m.key_count)]
5116
+ ];
5117
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5118
+ import_Paper8.default,
5119
+ {
5120
+ role: "listitem",
5121
+ variant: "outlined",
5122
+ sx: { borderRadius: 1.5, p: 1.5, minWidth: 0 },
5123
+ children: [
5124
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 1, children: [
5125
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { minWidth: 0, children: m.user_id === null ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Tooltip5.default, { title: "Activity of keys that were deleted or have no owner", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", sx: { fontStyle: "italic" }, children: "Unattributed" }) }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
5126
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", fontWeight: 600, noWrap: true, children: name }),
5127
+ sub && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", noWrap: true, sx: { ...monoSx, display: "block" }, children: sub })
5128
+ ] }) }),
5129
+ m.role && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })
5130
+ ] }),
5131
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mt: 1.5, display: "flex", alignItems: "baseline", justifyContent: "space-between", children: [
5132
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "h6", component: "span", sx: { lineHeight: 1.2 }, children: hidden ? fmtShare(m.spend_share_pct) : fmtUsd2(m.spend) }),
5133
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: hidden ? "of team usage" : `${fmtShare(m.spend_share_pct)} of team` })
5134
+ ] }),
5135
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { mt: 0.75, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Meter, { value: m.spend_share_pct, ariaLabel: `${name ?? "Unattributed"} share of team spend` }) }),
5136
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { mt: 1.5, display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0.5, children: stats.map(([label, value]) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5137
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", display: "block", children: label }),
5138
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", children: value })
5139
+ ] }, label)) })
5140
+ ]
5141
+ },
5142
+ m.user_id ?? "__unattributed"
5143
+ );
5144
+ })
5145
+ }
5146
+ )
5147
+ ] });
5125
5148
  };
5126
5149
  TeamKeyUsageTable = ({ usage }) => {
5127
5150
  const hidden = usage.budget_hidden === true;
@@ -5163,7 +5186,7 @@ var init_TeamUsage = __esm({
5163
5186
  }) })
5164
5187
  ] }) });
5165
5188
  };
5166
- TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, keyUsage, keyUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5189
+ TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, memberUsageFailed, keyUsage, keyUsageLoading, keyUsageFailed, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5167
5190
  const [expanded, setExpanded] = (0, import_react10.useState)(false);
5168
5191
  const chart = useChartTheme();
5169
5192
  const handleExpandChange = (newExpanded) => {
@@ -5362,15 +5385,19 @@ var init_TeamUsage = __esm({
5362
5385
  renderNamedTags("Knowledge bases", team.object_permission?.vector_stores, vectorStores),
5363
5386
  renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5364
5387
  renderDailySpendSection(),
5365
- (memberUsageLoading || memberUsage) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5388
+ (memberUsageLoading || memberUsage || memberUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5366
5389
  sectionLabel("Usage by member"),
5367
- 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 }) })
5390
+ memberUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MemberUsageCards, { usage: memberUsage }),
5391
+ !memberUsage && memberUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-member usage" }),
5392
+ !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 }) })
5368
5393
  ] }),
5369
- (keyUsageLoading || keyUsage) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5394
+ (keyUsageLoading || keyUsage || keyUsageFailed) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5370
5395
  sectionLabel("Keys by member"),
5371
- keyUsage ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TeamKeyUsageTable, { usage: keyUsage }) : /* @__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 }) })
5396
+ keyUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TeamKeyUsageTable, { usage: keyUsage }),
5397
+ !keyUsage && keyUsageFailed && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "caption", color: "text.secondary", children: "Could not load per-key usage" }),
5398
+ !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 }) })
5372
5399
  ] }),
5373
- /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5400
+ !memberUsage && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
5374
5401
  sectionLabel("Members"),
5375
5402
  team.members_with_roles?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
5376
5403
  import_TableContainer4.default,
@@ -5428,6 +5455,8 @@ var init_TeamUsage = __esm({
5428
5455
  getTeamMemberUsageLoading,
5429
5456
  getTeamKeyUsage,
5430
5457
  getTeamKeyUsageLoading,
5458
+ getTeamMemberUsageFailed,
5459
+ getTeamKeyUsageFailed,
5431
5460
  canManage,
5432
5461
  onEditTeam,
5433
5462
  canCreate,
@@ -5493,6 +5522,8 @@ var init_TeamUsage = __esm({
5493
5522
  memberUsageLoading: getTeamMemberUsageLoading?.(team.team_id) ?? false,
5494
5523
  keyUsage: getTeamKeyUsage?.(team.team_id) ?? null,
5495
5524
  keyUsageLoading: getTeamKeyUsageLoading?.(team.team_id) ?? false,
5525
+ memberUsageFailed: getTeamMemberUsageFailed?.(team.team_id) ?? false,
5526
+ keyUsageFailed: getTeamKeyUsageFailed?.(team.team_id) ?? false,
5496
5527
  canManage,
5497
5528
  onEditTeam,
5498
5529
  canManageMembers: canManageMembers?.(team),
@@ -6316,7 +6347,7 @@ function initDateRange() {
6316
6347
  else start.setDate(start.getDate() - 7);
6317
6348
  return { start, end };
6318
6349
  }
6319
- 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;
6350
+ 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;
6320
6351
  var init_LiteLLMPage = __esm({
6321
6352
  "src/components/LiteLLMPage.tsx"() {
6322
6353
  "use strict";
@@ -6356,6 +6387,7 @@ var init_LiteLLMPage = __esm({
6356
6387
  PERIOD_LS_KEY2 = "litellm_usage_period";
6357
6388
  PAGE_TABS = ["overview", "keys", "teams", "models", "insights"];
6358
6389
  isPageTab = (v) => !!v && PAGE_TABS.includes(v);
6390
+ isRefusal = (err) => err instanceof ApiError && (err.status === 403 || err.status === 404);
6359
6391
  LiteLLMPage = () => {
6360
6392
  const api = (0, import_core_plugin_api5.useApi)(liteLlmApiRef);
6361
6393
  const alertApi = (0, import_core_plugin_api5.useApi)(import_core_plugin_api5.alertApiRef);
@@ -6441,6 +6473,8 @@ var init_LiteLLMPage = __esm({
6441
6473
  const [memberUsageLoading, setMemberUsageLoading] = (0, import_react13.useState)({});
6442
6474
  const [keyUsageCache, setKeyUsageCache] = (0, import_react13.useState)({});
6443
6475
  const [keyUsageLoading, setKeyUsageLoading] = (0, import_react13.useState)({});
6476
+ const [memberUsageFailed, setMemberUsageFailed] = (0, import_react13.useState)({});
6477
+ const [keyUsageFailed, setKeyUsageFailed] = (0, import_react13.useState)({});
6444
6478
  const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use2.useAsync)(
6445
6479
  () => api.getUserInfo(),
6446
6480
  [api]
@@ -6509,6 +6543,8 @@ var init_LiteLLMPage = __esm({
6509
6543
  setMemberUsageLoading({});
6510
6544
  setKeyUsageCache({});
6511
6545
  setKeyUsageLoading({});
6546
+ setMemberUsageFailed({});
6547
+ setKeyUsageFailed({});
6512
6548
  }, [setDateRange, setCurrentPreset]);
6513
6549
  const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
6514
6550
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
@@ -6532,8 +6568,9 @@ var init_LiteLLMPage = __esm({
6532
6568
  const endDate = toLocalDay(dateRange.end);
6533
6569
  const data = await api.getTeamMemberUsage(teamId, startDate, endDate);
6534
6570
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: data }));
6535
- } catch {
6571
+ } catch (err) {
6536
6572
  setMemberUsageCache((prev) => ({ ...prev, [teamId]: null }));
6573
+ setMemberUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6537
6574
  } finally {
6538
6575
  setMemberUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6539
6576
  }
@@ -6546,8 +6583,9 @@ var init_LiteLLMPage = __esm({
6546
6583
  const endDate = toLocalDay(dateRange.end);
6547
6584
  const data = await api.getTeamKeyUsage(teamId, startDate, endDate);
6548
6585
  setKeyUsageCache((prev) => ({ ...prev, [teamId]: data }));
6549
- } catch {
6586
+ } catch (err) {
6550
6587
  setKeyUsageCache((prev) => ({ ...prev, [teamId]: null }));
6588
+ setKeyUsageFailed((prev) => ({ ...prev, [teamId]: !isRefusal(err) }));
6551
6589
  } finally {
6552
6590
  setKeyUsageLoading((prev) => ({ ...prev, [teamId]: false }));
6553
6591
  }
@@ -6851,6 +6889,8 @@ var init_LiteLLMPage = __esm({
6851
6889
  getTeamMemberUsageLoading: (teamId) => memberUsageLoading[teamId] ?? false,
6852
6890
  getTeamKeyUsage: (teamId) => keyUsageCache[teamId] ?? null,
6853
6891
  getTeamKeyUsageLoading: (teamId) => keyUsageLoading[teamId] ?? false,
6892
+ getTeamMemberUsageFailed: (teamId) => memberUsageFailed[teamId] ?? false,
6893
+ getTeamKeyUsageFailed: (teamId) => keyUsageFailed[teamId] ?? false,
6854
6894
  canManage: teamMgmtEnabled && canManageTeam && !readOnly,
6855
6895
  onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
6856
6896
  canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,