@acarmisc/backstage-plugin-litellm 0.43.0 → 0.44.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 +4 -1
- package/dist/components/TeamUsage.d.ts +11 -1
- package/dist/index.cjs.js +80 -3
- package/dist/index.cjs.js.map +2 -2
- package/dist/index.esm.js +80 -3
- package/dist/index.esm.js.map +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
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>;
|
|
@@ -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,9 @@ 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;
|
|
18
28
|
canManage?: boolean;
|
|
19
29
|
onEditTeam?: (team: TeamInfo) => void;
|
|
20
30
|
/** 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
|
-
|
|
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, keyUsage, keyUsageLoading, 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) => {
|
|
@@ -5320,6 +5366,10 @@ var init_TeamUsage = __esm({
|
|
|
5320
5366
|
sectionLabel("Usage by member"),
|
|
5321
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 }) })
|
|
5322
5368
|
] }),
|
|
5369
|
+
(keyUsageLoading || keyUsage) && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { mb: 2.5, children: [
|
|
5370
|
+
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 }) })
|
|
5372
|
+
] }),
|
|
5323
5373
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_Box14.default, { children: [
|
|
5324
5374
|
sectionLabel("Members"),
|
|
5325
5375
|
team.members_with_roles?.length ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
@@ -5376,6 +5426,8 @@ var init_TeamUsage = __esm({
|
|
|
5376
5426
|
getTeamUsageLoading,
|
|
5377
5427
|
getTeamMemberUsage,
|
|
5378
5428
|
getTeamMemberUsageLoading,
|
|
5429
|
+
getTeamKeyUsage,
|
|
5430
|
+
getTeamKeyUsageLoading,
|
|
5379
5431
|
canManage,
|
|
5380
5432
|
onEditTeam,
|
|
5381
5433
|
canCreate,
|
|
@@ -5439,6 +5491,8 @@ var init_TeamUsage = __esm({
|
|
|
5439
5491
|
usageLoading: getTeamUsageLoading(team.team_id),
|
|
5440
5492
|
memberUsage: getTeamMemberUsage?.(team.team_id) ?? null,
|
|
5441
5493
|
memberUsageLoading: getTeamMemberUsageLoading?.(team.team_id) ?? false,
|
|
5494
|
+
keyUsage: getTeamKeyUsage?.(team.team_id) ?? null,
|
|
5495
|
+
keyUsageLoading: getTeamKeyUsageLoading?.(team.team_id) ?? false,
|
|
5442
5496
|
canManage,
|
|
5443
5497
|
onEditTeam,
|
|
5444
5498
|
canManageMembers: canManageMembers?.(team),
|
|
@@ -6385,6 +6439,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6385
6439
|
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react13.useState)({});
|
|
6386
6440
|
const [memberUsageCache, setMemberUsageCache] = (0, import_react13.useState)({});
|
|
6387
6441
|
const [memberUsageLoading, setMemberUsageLoading] = (0, import_react13.useState)({});
|
|
6442
|
+
const [keyUsageCache, setKeyUsageCache] = (0, import_react13.useState)({});
|
|
6443
|
+
const [keyUsageLoading, setKeyUsageLoading] = (0, import_react13.useState)({});
|
|
6388
6444
|
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use2.useAsync)(
|
|
6389
6445
|
() => api.getUserInfo(),
|
|
6390
6446
|
[api]
|
|
@@ -6451,6 +6507,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6451
6507
|
setTeamUsageLoading({});
|
|
6452
6508
|
setMemberUsageCache({});
|
|
6453
6509
|
setMemberUsageLoading({});
|
|
6510
|
+
setKeyUsageCache({});
|
|
6511
|
+
setKeyUsageLoading({});
|
|
6454
6512
|
}, [setDateRange, setCurrentPreset]);
|
|
6455
6513
|
const loadTeamUsage = (0, import_react13.useCallback)(async (teamId) => {
|
|
6456
6514
|
if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
|
|
@@ -6480,6 +6538,20 @@ var init_LiteLLMPage = __esm({
|
|
|
6480
6538
|
setMemberUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
6481
6539
|
}
|
|
6482
6540
|
}, [api, dateRange, memberUsageCache, memberUsageLoading]);
|
|
6541
|
+
const loadKeyUsage = (0, import_react13.useCallback)(async (teamId) => {
|
|
6542
|
+
if (keyUsageCache[teamId] !== void 0 || keyUsageLoading[teamId]) return;
|
|
6543
|
+
setKeyUsageLoading((prev) => ({ ...prev, [teamId]: true }));
|
|
6544
|
+
try {
|
|
6545
|
+
const startDate = toLocalDay(dateRange.start);
|
|
6546
|
+
const endDate = toLocalDay(dateRange.end);
|
|
6547
|
+
const data = await api.getTeamKeyUsage(teamId, startDate, endDate);
|
|
6548
|
+
setKeyUsageCache((prev) => ({ ...prev, [teamId]: data }));
|
|
6549
|
+
} catch {
|
|
6550
|
+
setKeyUsageCache((prev) => ({ ...prev, [teamId]: null }));
|
|
6551
|
+
} finally {
|
|
6552
|
+
setKeyUsageLoading((prev) => ({ ...prev, [teamId]: false }));
|
|
6553
|
+
}
|
|
6554
|
+
}, [api, dateRange, keyUsageCache, keyUsageLoading]);
|
|
6483
6555
|
const allowedModels = (0, import_react13.useMemo)(() => {
|
|
6484
6556
|
if (!allModels?.length) return [];
|
|
6485
6557
|
const userModels = userInfo?.models;
|
|
@@ -6777,6 +6849,8 @@ var init_LiteLLMPage = __esm({
|
|
|
6777
6849
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
6778
6850
|
getTeamMemberUsage: (teamId) => memberUsageCache[teamId] ?? null,
|
|
6779
6851
|
getTeamMemberUsageLoading: (teamId) => memberUsageLoading[teamId] ?? false,
|
|
6852
|
+
getTeamKeyUsage: (teamId) => keyUsageCache[teamId] ?? null,
|
|
6853
|
+
getTeamKeyUsageLoading: (teamId) => keyUsageLoading[teamId] ?? false,
|
|
6780
6854
|
canManage: teamMgmtEnabled && canManageTeam && !readOnly,
|
|
6781
6855
|
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
6782
6856
|
canCreate: teamMgmtEnabled && canCreateTeam && !readOnly,
|
|
@@ -6788,7 +6862,10 @@ var init_LiteLLMPage = __esm({
|
|
|
6788
6862
|
readOnly,
|
|
6789
6863
|
onTeamExpand: (team) => {
|
|
6790
6864
|
loadTeamUsage(team.team_id);
|
|
6791
|
-
if (liteLlmConfig?.teamUsage?.memberBreakdown)
|
|
6865
|
+
if (liteLlmConfig?.teamUsage?.memberBreakdown) {
|
|
6866
|
+
loadMemberUsage(team.team_id);
|
|
6867
|
+
loadKeyUsage(team.team_id);
|
|
6868
|
+
}
|
|
6792
6869
|
}
|
|
6793
6870
|
}
|
|
6794
6871
|
);
|