@acarmisc/backstage-plugin-litellm 0.41.0 → 0.43.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
@@ -37,6 +37,11 @@ export interface LiteLlmApiInterface {
37
37
  }): Promise<TeamInfo>;
38
38
  removeTeamMember(teamId: string, userEntityRef: string): Promise<TeamInfo>;
39
39
  getVectorStores(): Promise<VectorStoreInfo[]>;
40
+ /** Id → name for every KB and MCP server, for labels only (not allowlist-filtered). */
41
+ getObjectNames(): Promise<{
42
+ vector_stores: VectorStoreInfo[];
43
+ mcp_servers: McpServerInfo[];
44
+ }>;
40
45
  setTeamKnowledgeBases(teamId: string, vectorStores: string[]): Promise<TeamInfo>;
41
46
  getMcpServers(): Promise<McpServerInfo[]>;
42
47
  setTeamMcpServers(teamId: string, mcpServers: string[]): Promise<TeamInfo>;
@@ -105,6 +110,10 @@ export declare class LiteLlmApi implements LiteLlmApiInterface {
105
110
  }): Promise<TeamInfo>;
106
111
  removeTeamMember(teamId: string, userEntityRef: string): Promise<TeamInfo>;
107
112
  getVectorStores(): Promise<VectorStoreInfo[]>;
113
+ getObjectNames(): Promise<{
114
+ vector_stores: VectorStoreInfo[];
115
+ mcp_servers: McpServerInfo[];
116
+ }>;
108
117
  setTeamKnowledgeBases(teamId: string, vectorStores: string[]): Promise<TeamInfo>;
109
118
  getMcpServers(): Promise<McpServerInfo[]>;
110
119
  setTeamMcpServers(teamId: string, mcpServers: string[]): Promise<TeamInfo>;
@@ -1,6 +1,6 @@
1
1
  import { FC } from 'react';
2
2
  import { type KeyFilterType } from '../keyFilter';
3
- import { VirtualKey } from '../types';
3
+ import { TeamInfo, VirtualKey } from '../types';
4
4
  interface KeysTableProps {
5
5
  keys: VirtualKey[];
6
6
  /** Opens the shared key form dialog in create mode (rendered at page level). */
@@ -16,6 +16,8 @@ interface KeysTableProps {
16
16
  }>;
17
17
  filter?: KeyFilterType;
18
18
  onClearFilter?: () => void;
19
+ /** Used to show team names instead of raw team ids. */
20
+ teams?: TeamInfo[];
19
21
  }
20
22
  export declare const KeysTable: FC<KeysTableProps>;
21
23
  export {};
@@ -27,12 +27,16 @@ interface ManageTeamDialogProps {
27
27
  canManageKnowledgeBases?: boolean;
28
28
  /** Vector stores the caller may attach (already allowlist-filtered server-side). */
29
29
  vectorStores?: VectorStoreInfo[];
30
+ /** Names for labelling attached stores; may include stores not offered in the picker. */
31
+ vectorStoreNames?: VectorStoreInfo[];
30
32
  /** Called to replace the team's attached knowledge bases; parent refreshes `team`. */
31
33
  onSaveKnowledgeBases?: (vectorStoreIds: string[]) => Promise<void>;
32
34
  /** When true, the MCP Servers section (edit mode only) is shown. */
33
35
  canManageMcpServers?: boolean;
34
36
  /** MCP servers the caller may attach (already allowlist-filtered server-side). */
35
37
  mcpServers?: McpServerInfo[];
38
+ /** Names for labelling attached servers; may include servers not offered in the picker. */
39
+ mcpServerNames?: McpServerInfo[];
36
40
  /** Called to replace the team's attached MCP servers; parent refreshes `team`. */
37
41
  onSaveMcpServers?: (mcpServerIds: string[]) => Promise<void>;
38
42
  /** When true, only members section is shown (not budget/models/KB/MCP); title changes; Save becomes Done. */
@@ -1,5 +1,5 @@
1
1
  import { FC } from 'react';
2
- import { TeamInfo, TeamMemberUsage, UsageMetrics } from '../types';
2
+ import { McpServerInfo, TeamInfo, 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.
@@ -27,6 +27,10 @@ interface TeamUsageProps {
27
27
  onManageMembers?: (team: TeamInfo) => void;
28
28
  /** Teams are synced from an identity provider: show a badge, no write controls. */
29
29
  readOnly?: boolean;
30
+ /** Used to show knowledge base names instead of raw ids. */
31
+ vectorStores?: VectorStoreInfo[];
32
+ /** Used to show MCP server names instead of raw ids. */
33
+ mcpServers?: McpServerInfo[];
30
34
  }
31
35
  export declare const TeamUsage: FC<TeamUsageProps>;
32
36
  export {};
package/dist/index.cjs.js CHANGED
@@ -287,6 +287,10 @@ var init_api = __esm({
287
287
  async getVectorStores() {
288
288
  return this.get("/vector-stores");
289
289
  }
290
+ // Names only, readable by any signed-in user; never used for the picker.
291
+ async getObjectNames() {
292
+ return this.get("/object-names");
293
+ }
290
294
  async setTeamKnowledgeBases(teamId, vectorStores) {
291
295
  return this.put(
292
296
  `/teams/${encodeURIComponent(teamId)}/knowledge-bases`,
@@ -1782,7 +1786,8 @@ var init_KeysTable = __esm({
1782
1786
  onDeleteKey,
1783
1787
  onPruneExpiredKeys,
1784
1788
  filter,
1785
- onClearFilter
1789
+ onClearFilter,
1790
+ teams
1786
1791
  }) => {
1787
1792
  const managePermission = (0, import_plugin_permission_react.usePermission)({ permission: import_backstage_plugin_litellm_common.litellmKeyManagePermission });
1788
1793
  const revokePermission = (0, import_plugin_permission_react.usePermission)({ permission: import_backstage_plugin_litellm_common.litellmKeyRevokePermission });
@@ -1810,9 +1815,14 @@ var init_KeysTable = __esm({
1810
1815
  setSortDirection("asc");
1811
1816
  }
1812
1817
  };
1818
+ const teamNames = (0, import_react4.useMemo)(
1819
+ () => new Map((teams ?? []).map((t) => [t.team_id, t.team_alias || t.team_id])),
1820
+ [teams]
1821
+ );
1822
+ const teamLabel = (id) => teamNames.get(id) ?? id;
1813
1823
  const teamIds = (0, import_react4.useMemo)(
1814
- () => [...new Set(keys.map((k) => k.team_id).filter((t) => !!t))].sort(),
1815
- [keys]
1824
+ () => [...new Set(keys.map((k) => k.team_id).filter((t) => !!t))].sort((a, b) => (teamNames.get(a) ?? a).localeCompare(teamNames.get(b) ?? b)),
1825
+ [keys, teamNames]
1816
1826
  );
1817
1827
  const filteredKeys = (0, import_react4.useMemo)(() => {
1818
1828
  let result = keys;
@@ -1936,7 +1946,7 @@ var init_KeysTable = __esm({
1936
1946
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_Typography5.default, { variant: "body2", children: key.key_alias || "\u2014" }),
1937
1947
  key.blocked && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusPill, { label: "Blocked", tone: "danger" })
1938
1948
  ] }) }),
1939
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_TableCell.default, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_Typography5.default, { variant: "body2", color: "text.secondary", children: key.team_id || "\u2014" }) }),
1949
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_TableCell.default, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_Typography5.default, { variant: "body2", color: "text.secondary", children: key.team_id ? teamLabel(key.team_id) : "\u2014" }) }),
1940
1950
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_TableCell.default, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_Box7.default, { display: "flex", alignItems: "center", gap: 0.5, children: [
1941
1951
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1942
1952
  import_Typography5.default,
@@ -2057,7 +2067,7 @@ var init_KeysTable = __esm({
2057
2067
  sx: { minWidth: 160 },
2058
2068
  children: [
2059
2069
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_MenuItem.default, { value: "", children: "All teams" }),
2060
- teamIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_MenuItem.default, { value: id, children: id }, id))
2070
+ teamIds.map((id) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_MenuItem.default, { value: id, children: teamLabel(id) }, id))
2061
2071
  ]
2062
2072
  }
2063
2073
  ),
@@ -3239,9 +3249,11 @@ var init_ManageTeamDialog = __esm({
3239
3249
  onRemoveMember,
3240
3250
  canManageKnowledgeBases,
3241
3251
  vectorStores,
3252
+ vectorStoreNames,
3242
3253
  onSaveKnowledgeBases,
3243
3254
  canManageMcpServers,
3244
3255
  mcpServers,
3256
+ mcpServerNames,
3245
3257
  onSaveMcpServers,
3246
3258
  membersOnly,
3247
3259
  currentUserId,
@@ -3510,10 +3522,10 @@ var init_ManageTeamDialog = __esm({
3510
3522
  const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
3511
3523
  const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
3512
3524
  const kbOptions = (vectorStores ?? []).map((v) => v.id);
3513
- const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
3525
+ const kbLabel = (id) => (vectorStoreNames ?? []).find((v) => v.id === id)?.name ?? id;
3514
3526
  const showMcpServers = mode === "edit" && !!canManageMcpServers;
3515
3527
  const mcpOptions = (mcpServers ?? []).map((s) => s.id);
3516
- const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
3528
+ const mcpLabel = (id) => (mcpServerNames ?? []).find((s) => s.id === id)?.name ?? id;
3517
3529
  const isRemovalDisabled = (memberUserId, memberRole) => {
3518
3530
  if (!membersOnly) return { disabled: false };
3519
3531
  if (memberUserId === currentUserId) {
@@ -5105,7 +5117,7 @@ var init_TeamUsage = __esm({
5105
5117
  }) })
5106
5118
  ] }) });
5107
5119
  };
5108
- TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand }) => {
5120
+ TeamCard = ({ team, usage, usageLoading, memberUsage, memberUsageLoading, canManage, onEditTeam, canManageMembers, onManageMembers, onExpand, vectorStores, mcpServers }) => {
5109
5121
  const [expanded, setExpanded] = (0, import_react10.useState)(false);
5110
5122
  const chart = useChartTheme();
5111
5123
  const handleExpandChange = (newExpanded) => {
@@ -5172,6 +5184,10 @@ var init_TeamUsage = __esm({
5172
5184
  children: label
5173
5185
  }
5174
5186
  );
5187
+ const renderNamedTags = (label, ids, items) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5188
+ sectionLabel(label),
5189
+ ids?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { display: "flex", gap: 0.5, flexWrap: "wrap", children: ids.map((id) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TagChip, { label: items?.find((i) => i.id === id)?.name ?? id, title: id }, id)) }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "None configured" })
5190
+ ] });
5175
5191
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Paper8.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 }, children: [
5176
5192
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { display: "flex", alignItems: "center", gap: 1.5, children: [
5177
5193
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
@@ -5297,6 +5313,8 @@ var init_TeamUsage = __esm({
5297
5313
  sectionLabel("Models"),
5298
5314
  team.models?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Box14.default, { display: "flex", gap: 0.5, flexWrap: "wrap", children: team.models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(TagChip, { label: m, title: m }, m)) }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Typography12.default, { variant: "body2", color: "text.secondary", children: "All models allowed" })
5299
5315
  ] }),
5316
+ renderNamedTags("Knowledge bases", team.object_permission?.vector_stores, vectorStores),
5317
+ renderNamedTags("MCP servers", team.object_permission?.mcp_servers, mcpServers),
5300
5318
  renderDailySpendSection(),
5301
5319
  (memberUsageLoading || memberUsage) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
5302
5320
  sectionLabel("Usage by member"),
@@ -5365,7 +5383,9 @@ var init_TeamUsage = __esm({
5365
5383
  onTeamExpand,
5366
5384
  canManageMembers,
5367
5385
  onManageMembers,
5368
- readOnly
5386
+ readOnly,
5387
+ vectorStores,
5388
+ mcpServers
5369
5389
  }) => {
5370
5390
  const readOnlyBadge = /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_Tooltip5.default, { title: "Teams and memberships are managed in your identity provider. Changes made here would be overwritten, so editing is disabled.", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
5371
5391
  import_Box14.default,
@@ -5423,7 +5443,9 @@ var init_TeamUsage = __esm({
5423
5443
  onEditTeam,
5424
5444
  canManageMembers: canManageMembers?.(team),
5425
5445
  onManageMembers,
5426
- onExpand: onTeamExpand
5446
+ onExpand: onTeamExpand,
5447
+ vectorStores,
5448
+ mcpServers
5427
5449
  },
5428
5450
  team.team_id
5429
5451
  )) })
@@ -6392,12 +6414,16 @@ var init_LiteLLMPage = __esm({
6392
6414
  const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
6393
6415
  const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
6394
6416
  const { value: vectorStores } = (0, import_react_use2.useAsync)(
6395
- async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
6396
- [api, objectPermsEnabled]
6417
+ () => api.getVectorStores().catch(() => []),
6418
+ [api]
6397
6419
  );
6398
6420
  const { value: mcpServers } = (0, import_react_use2.useAsync)(
6399
- async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
6400
- [api, objectPermsEnabled]
6421
+ () => api.getMcpServers().catch(() => []),
6422
+ [api]
6423
+ );
6424
+ const { value: objectNames } = (0, import_react_use2.useAsync)(
6425
+ () => api.getObjectNames().catch(() => ({ vector_stores: [], mcp_servers: [] })),
6426
+ [api]
6401
6427
  );
6402
6428
  const teams = (0, import_react13.useMemo)(() => {
6403
6429
  if (!allTeams?.length) return [];
@@ -6723,7 +6749,8 @@ var init_LiteLLMPage = __esm({
6723
6749
  onDeleteKey: handleDeleteKey,
6724
6750
  onPruneExpiredKeys: handlePruneExpiredKeys,
6725
6751
  filter,
6726
- onClearFilter: clearFilter
6752
+ onClearFilter: clearFilter,
6753
+ teams: allTeams ?? []
6727
6754
  }
6728
6755
  );
6729
6756
  })(),
@@ -6754,6 +6781,8 @@ var init_LiteLLMPage = __esm({
6754
6781
  onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
6755
6782
  canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
6756
6783
  onCreateTeam: () => setManageTeam({ mode: "create" }),
6784
+ vectorStores: objectNames?.vector_stores ?? [],
6785
+ mcpServers: objectNames?.mcp_servers ?? [],
6757
6786
  canManageMembers: (t) => teamMgmtEnabled && canManageMembers && isTeamMemberManager(t, userInfo?.user_id, memberManagerRoles, readOnly),
6758
6787
  onManageMembers: (t) => setManageTeam({ mode: "members", team: t }),
6759
6788
  readOnly,
@@ -6885,6 +6914,7 @@ var init_LiteLLMPage = __esm({
6885
6914
  },
6886
6915
  canManageKnowledgeBases: teamMgmtEnabled && objectPermsEnabled && canManageKnowledgeBases,
6887
6916
  vectorStores: vectorStores ?? [],
6917
+ vectorStoreNames: objectNames?.vector_stores ?? [],
6888
6918
  onSaveKnowledgeBases: async (vectorStoreIds) => {
6889
6919
  if (!manageTeam?.team) return;
6890
6920
  try {
@@ -6903,6 +6933,7 @@ var init_LiteLLMPage = __esm({
6903
6933
  },
6904
6934
  canManageMcpServers: teamMgmtEnabled && objectPermsEnabled && canManageMcpServers,
6905
6935
  mcpServers: mcpServers ?? [],
6936
+ mcpServerNames: objectNames?.mcp_servers ?? [],
6906
6937
  onSaveMcpServers: async (mcpServerIds) => {
6907
6938
  if (!manageTeam?.team) return;
6908
6939
  try {