@acarmisc/backstage-plugin-litellm 0.17.0 → 0.18.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/components/TeamUsage.d.ts +3 -0
- package/dist/index.cjs.js +233 -134
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.esm.js +242 -143
- package/dist/index.esm.js.map +3 -3
- package/package.json +4 -1
package/dist/index.cjs.js
CHANGED
|
@@ -1942,7 +1942,7 @@ var init_GenerateKeyDialog = __esm({
|
|
|
1942
1942
|
});
|
|
1943
1943
|
|
|
1944
1944
|
// src/components/ManageTeamDialog.tsx
|
|
1945
|
-
var import_react5, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete3, import_MenuItem2, import_Box5, import_Divider2, import_Typography5, import_IconButton3,
|
|
1945
|
+
var import_react5, import_Dialog3, import_DialogTitle3, import_DialogContent3, import_DialogActions3, import_TextField3, import_Button4, import_Alert2, import_Checkbox2, import_FormControlLabel2, import_Autocomplete3, import_CircularProgress3, import_MenuItem2, import_Box5, import_Divider2, import_Typography5, import_IconButton3, import_Paper3, import_Table2, import_TableBody2, import_TableCell2, import_TableContainer2, import_TableHead2, import_TableRow2, import_icons_material4, import_core_plugin_api2, import_plugin_catalog_react, import_catalog_model, import_react_use, BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
|
|
1946
1946
|
var init_ManageTeamDialog = __esm({
|
|
1947
1947
|
"src/components/ManageTeamDialog.tsx"() {
|
|
1948
1948
|
"use strict";
|
|
@@ -1957,15 +1957,25 @@ var init_ManageTeamDialog = __esm({
|
|
|
1957
1957
|
import_Checkbox2 = __toESM(require("@mui/material/Checkbox"));
|
|
1958
1958
|
import_FormControlLabel2 = __toESM(require("@mui/material/FormControlLabel"));
|
|
1959
1959
|
import_Autocomplete3 = __toESM(require("@mui/material/Autocomplete"));
|
|
1960
|
+
import_CircularProgress3 = __toESM(require("@mui/material/CircularProgress"));
|
|
1960
1961
|
import_MenuItem2 = __toESM(require("@mui/material/MenuItem"));
|
|
1961
1962
|
import_Box5 = __toESM(require("@mui/material/Box"));
|
|
1962
1963
|
import_Divider2 = __toESM(require("@mui/material/Divider"));
|
|
1963
1964
|
import_Typography5 = __toESM(require("@mui/material/Typography"));
|
|
1964
1965
|
import_IconButton3 = __toESM(require("@mui/material/IconButton"));
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1966
|
+
import_Paper3 = __toESM(require("@mui/material/Paper"));
|
|
1967
|
+
import_Table2 = __toESM(require("@mui/material/Table"));
|
|
1968
|
+
import_TableBody2 = __toESM(require("@mui/material/TableBody"));
|
|
1969
|
+
import_TableCell2 = __toESM(require("@mui/material/TableCell"));
|
|
1970
|
+
import_TableContainer2 = __toESM(require("@mui/material/TableContainer"));
|
|
1971
|
+
import_TableHead2 = __toESM(require("@mui/material/TableHead"));
|
|
1972
|
+
import_TableRow2 = __toESM(require("@mui/material/TableRow"));
|
|
1968
1973
|
import_icons_material4 = require("@mui/icons-material");
|
|
1974
|
+
import_core_plugin_api2 = require("@backstage/core-plugin-api");
|
|
1975
|
+
import_plugin_catalog_react = require("@backstage/plugin-catalog-react");
|
|
1976
|
+
import_catalog_model = require("@backstage/catalog-model");
|
|
1977
|
+
import_react_use = require("react-use");
|
|
1978
|
+
init_ui();
|
|
1969
1979
|
BUDGET_DURATION_OPTIONS = [
|
|
1970
1980
|
{ value: "1d", label: "Daily (1d)" },
|
|
1971
1981
|
{ value: "7d", label: "Weekly (7d)" },
|
|
@@ -2000,6 +2010,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2000
2010
|
const [budgetDuration, setBudgetDuration] = (0, import_react5.useState)("");
|
|
2001
2011
|
const [submitting, setSubmitting] = (0, import_react5.useState)(false);
|
|
2002
2012
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
2013
|
+
const [memberQuery, setMemberQuery] = (0, import_react5.useState)("");
|
|
2003
2014
|
const [memberRef, setMemberRef] = (0, import_react5.useState)("");
|
|
2004
2015
|
const [memberBudget, setMemberBudget] = (0, import_react5.useState)("");
|
|
2005
2016
|
const [memberBusy, setMemberBusy] = (0, import_react5.useState)(false);
|
|
@@ -2012,6 +2023,36 @@ var init_ManageTeamDialog = __esm({
|
|
|
2012
2023
|
const [mcpError, setMcpError] = (0, import_react5.useState)(null);
|
|
2013
2024
|
const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
|
|
2014
2025
|
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
|
|
2026
|
+
const members = (0, import_react5.useMemo)(() => team?.members_with_roles ?? [], [team]);
|
|
2027
|
+
const showMembers = mode === "edit" && !!canManageMembers;
|
|
2028
|
+
const catalogApi = (0, import_core_plugin_api2.useApi)(import_plugin_catalog_react.catalogApiRef);
|
|
2029
|
+
const { value: catalogUsers, loading: usersLoading } = (0, import_react_use.useAsync)(async () => {
|
|
2030
|
+
if (!open || !showMembers) return [];
|
|
2031
|
+
try {
|
|
2032
|
+
const { items } = await catalogApi.getEntities({
|
|
2033
|
+
filter: { kind: "User" },
|
|
2034
|
+
fields: ["kind", "metadata.namespace", "metadata.name", "metadata.title", "spec.profile"]
|
|
2035
|
+
});
|
|
2036
|
+
return items;
|
|
2037
|
+
} catch {
|
|
2038
|
+
return [];
|
|
2039
|
+
}
|
|
2040
|
+
}, [open, showMembers, catalogApi]);
|
|
2041
|
+
const userOptions = (0, import_react5.useMemo)(() => {
|
|
2042
|
+
const existingEmails = new Set(
|
|
2043
|
+
members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
|
|
2044
|
+
);
|
|
2045
|
+
return (catalogUsers ?? []).map((e) => {
|
|
2046
|
+
const ref = (0, import_catalog_model.stringifyEntityRef)(e);
|
|
2047
|
+
const displayName = e.spec?.profile?.displayName || e.metadata.title || e.metadata.name;
|
|
2048
|
+
const email = e.spec?.profile?.email;
|
|
2049
|
+
return {
|
|
2050
|
+
ref,
|
|
2051
|
+
email,
|
|
2052
|
+
label: email ? `${displayName} (${email})` : displayName
|
|
2053
|
+
};
|
|
2054
|
+
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
|
|
2055
|
+
}, [catalogUsers, members]);
|
|
2015
2056
|
(0, import_react5.useEffect)(() => {
|
|
2016
2057
|
if (open) {
|
|
2017
2058
|
if (mode === "edit" && team) {
|
|
@@ -2030,6 +2071,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2030
2071
|
setBudgetDuration(DEFAULT_BUDGET_DURATION);
|
|
2031
2072
|
}
|
|
2032
2073
|
setError(null);
|
|
2074
|
+
setMemberQuery("");
|
|
2033
2075
|
setMemberRef("");
|
|
2034
2076
|
setMemberBudget("");
|
|
2035
2077
|
setMemberError(null);
|
|
@@ -2101,6 +2143,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2101
2143
|
try {
|
|
2102
2144
|
setMemberBusy(true);
|
|
2103
2145
|
await onAddMember(memberRef.trim(), budget);
|
|
2146
|
+
setMemberQuery("");
|
|
2104
2147
|
setMemberRef("");
|
|
2105
2148
|
setMemberBudget("");
|
|
2106
2149
|
} catch (err) {
|
|
@@ -2147,8 +2190,6 @@ var init_ManageTeamDialog = __esm({
|
|
|
2147
2190
|
};
|
|
2148
2191
|
const modelNames = allModels.map((m) => m.model_name);
|
|
2149
2192
|
const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
|
|
2150
|
-
const members = team?.members_with_roles ?? [];
|
|
2151
|
-
const showMembers = mode === "edit" && !!canManageMembers;
|
|
2152
2193
|
const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
|
|
2153
2194
|
const kbOptions = (vectorStores ?? []).map((v) => v.id);
|
|
2154
2195
|
const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
|
|
@@ -2172,7 +2213,15 @@ var init_ManageTeamDialog = __esm({
|
|
|
2172
2213
|
value: models,
|
|
2173
2214
|
onChange: (_, newValue) => setModels(newValue),
|
|
2174
2215
|
disabled: submitting,
|
|
2175
|
-
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
2216
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
2217
|
+
import_TextField3.default,
|
|
2218
|
+
{
|
|
2219
|
+
...params,
|
|
2220
|
+
label: "Models",
|
|
2221
|
+
required: true,
|
|
2222
|
+
helperText: "At least one model is required here \u2014 this delegated flow only grants access to models your admin has allow-listed for team creation, not every proxy model. (Teams with no model restriction shown elsewhere were configured directly in LiteLLM.)"
|
|
2223
|
+
}
|
|
2224
|
+
)
|
|
2176
2225
|
}
|
|
2177
2226
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2178
2227
|
import_TextField3.default,
|
|
@@ -2204,34 +2253,69 @@ var init_ManageTeamDialog = __esm({
|
|
|
2204
2253
|
fullWidth: true
|
|
2205
2254
|
},
|
|
2206
2255
|
durationOptions.map((o) => /* @__PURE__ */ import_react5.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
|
|
2207
|
-
), showMembers && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary" }, "No members yet.") : /* @__PURE__ */ import_react5.default.createElement(
|
|
2208
|
-
|
|
2256
|
+
), showMembers && /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, null, /* @__PURE__ */ import_react5.default.createElement(import_Divider2.default, { sx: { my: 1 } }), /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ import_react5.default.createElement(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ import_react5.default.createElement(import_TableContainer2.default, { component: import_Paper3.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ import_react5.default.createElement(import_Table2.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react5.default.createElement(import_TableHead2.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableRow2.default, null, /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, null, "User"), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, "Role"), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ import_react5.default.createElement(import_TableBody2.default, null, members.map((m) => /* @__PURE__ */ import_react5.default.createElement(import_TableRow2.default, { key: m.user_id }, /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, null, m.user_email ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_Typography5.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react5.default.createElement(
|
|
2257
|
+
import_Typography5.default,
|
|
2209
2258
|
{
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
import_IconButton3.default,
|
|
2214
|
-
{
|
|
2215
|
-
edge: "end",
|
|
2216
|
-
size: "small",
|
|
2217
|
-
"aria-label": `remove ${m.user_id}`,
|
|
2218
|
-
disabled: memberBusy,
|
|
2219
|
-
onClick: () => handleRemoveMember(m.user_id)
|
|
2220
|
-
},
|
|
2221
|
-
/* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
|
|
2222
|
-
)
|
|
2259
|
+
variant: "caption",
|
|
2260
|
+
color: "text.secondary",
|
|
2261
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2223
2262
|
},
|
|
2224
|
-
|
|
2225
|
-
))
|
|
2226
|
-
|
|
2263
|
+
m.user_id
|
|
2264
|
+
)) : /* @__PURE__ */ import_react5.default.createElement(
|
|
2265
|
+
import_Typography5.default,
|
|
2227
2266
|
{
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2267
|
+
variant: "body2",
|
|
2268
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2269
|
+
},
|
|
2270
|
+
m.user_id
|
|
2271
|
+
)), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react5.default.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ import_react5.default.createElement(import_TableCell2.default, { align: "right" }, /* @__PURE__ */ import_react5.default.createElement(
|
|
2272
|
+
import_IconButton3.default,
|
|
2273
|
+
{
|
|
2274
|
+
edge: "end",
|
|
2233
2275
|
size: "small",
|
|
2234
|
-
|
|
2276
|
+
"aria-label": `remove ${m.user_id}`,
|
|
2277
|
+
disabled: memberBusy,
|
|
2278
|
+
onClick: () => handleRemoveMember(m.user_id)
|
|
2279
|
+
},
|
|
2280
|
+
/* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
|
|
2281
|
+
))))))), /* @__PURE__ */ import_react5.default.createElement(import_Box5.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ import_react5.default.createElement(
|
|
2282
|
+
import_Autocomplete3.default,
|
|
2283
|
+
{
|
|
2284
|
+
freeSolo: true,
|
|
2285
|
+
autoHighlight: true,
|
|
2286
|
+
options: userOptions,
|
|
2287
|
+
loading: usersLoading,
|
|
2288
|
+
inputValue: memberQuery,
|
|
2289
|
+
onInputChange: (_, newInputValue, reason) => {
|
|
2290
|
+
setMemberQuery(newInputValue);
|
|
2291
|
+
if (reason === "input" || reason === "clear") {
|
|
2292
|
+
setMemberRef(newInputValue);
|
|
2293
|
+
}
|
|
2294
|
+
},
|
|
2295
|
+
onChange: (_, newValue) => {
|
|
2296
|
+
if (newValue && typeof newValue !== "string") {
|
|
2297
|
+
setMemberQuery(newValue.label);
|
|
2298
|
+
setMemberRef(newValue.ref);
|
|
2299
|
+
}
|
|
2300
|
+
},
|
|
2301
|
+
getOptionLabel: (o) => typeof o === "string" ? o : o.label,
|
|
2302
|
+
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
2303
|
+
disabled: memberBusy,
|
|
2304
|
+
sx: { flex: 1, minWidth: 260 },
|
|
2305
|
+
renderInput: (params) => /* @__PURE__ */ import_react5.default.createElement(
|
|
2306
|
+
import_TextField3.default,
|
|
2307
|
+
{
|
|
2308
|
+
...params,
|
|
2309
|
+
label: "Add member",
|
|
2310
|
+
placeholder: "Search by name or email\u2026",
|
|
2311
|
+
size: "small",
|
|
2312
|
+
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
2313
|
+
InputProps: {
|
|
2314
|
+
...params.InputProps,
|
|
2315
|
+
endAdornment: /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, usersLoading ? /* @__PURE__ */ import_react5.default.createElement(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
)
|
|
2235
2319
|
}
|
|
2236
2320
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2237
2321
|
import_TextField3.default,
|
|
@@ -2302,23 +2386,23 @@ function rateTone(rate) {
|
|
|
2302
2386
|
if (rate >= 0.9) return "warning";
|
|
2303
2387
|
return "danger";
|
|
2304
2388
|
}
|
|
2305
|
-
var import_react6,
|
|
2389
|
+
var import_react6, import_Paper4, import_Box6, import_Typography6, import_TextField4, import_MenuItem3, import_Grid, import_Table3, import_TableBody3, import_TableCell3, import_TableContainer3, import_TableHead3, import_TableRow3, import_Skeleton3, import_recharts, TOP_N_MODELS, PERIOD_LS_KEY, PRESET_LABELS, fmtPct, ChartSkeleton, ChartOrFallback, SuccessRateCell, UsageStats;
|
|
2306
2390
|
var init_UsageStats = __esm({
|
|
2307
2391
|
"src/components/UsageStats.tsx"() {
|
|
2308
2392
|
"use strict";
|
|
2309
2393
|
import_react6 = __toESM(require("react"));
|
|
2310
|
-
|
|
2394
|
+
import_Paper4 = __toESM(require("@mui/material/Paper"));
|
|
2311
2395
|
import_Box6 = __toESM(require("@mui/material/Box"));
|
|
2312
2396
|
import_Typography6 = __toESM(require("@mui/material/Typography"));
|
|
2313
2397
|
import_TextField4 = __toESM(require("@mui/material/TextField"));
|
|
2314
2398
|
import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
|
|
2315
2399
|
import_Grid = __toESM(require("@mui/material/Grid"));
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2400
|
+
import_Table3 = __toESM(require("@mui/material/Table"));
|
|
2401
|
+
import_TableBody3 = __toESM(require("@mui/material/TableBody"));
|
|
2402
|
+
import_TableCell3 = __toESM(require("@mui/material/TableCell"));
|
|
2403
|
+
import_TableContainer3 = __toESM(require("@mui/material/TableContainer"));
|
|
2404
|
+
import_TableHead3 = __toESM(require("@mui/material/TableHead"));
|
|
2405
|
+
import_TableRow3 = __toESM(require("@mui/material/TableRow"));
|
|
2322
2406
|
import_Skeleton3 = __toESM(require("@mui/material/Skeleton"));
|
|
2323
2407
|
import_recharts = require("recharts");
|
|
2324
2408
|
init_format();
|
|
@@ -2490,21 +2574,21 @@ var init_UsageStats = __esm({
|
|
|
2490
2574
|
];
|
|
2491
2575
|
const renderKeyRows = () => {
|
|
2492
2576
|
if (loading) {
|
|
2493
|
-
return /* @__PURE__ */ import_react6.default.createElement(
|
|
2577
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ import_react6.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
|
|
2494
2578
|
}
|
|
2495
2579
|
if (keyRows.length === 0) {
|
|
2496
|
-
return /* @__PURE__ */ import_react6.default.createElement(
|
|
2580
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 7 }, /* @__PURE__ */ import_react6.default.createElement(EmptyState, { message: "No key activity in this period" })));
|
|
2497
2581
|
}
|
|
2498
|
-
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2582
|
+
return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, { key: r.keyHash }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ import_react6.default.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ import_react6.default.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2499
2583
|
};
|
|
2500
2584
|
const renderModelRows = () => {
|
|
2501
2585
|
if (loading) {
|
|
2502
|
-
return /* @__PURE__ */ import_react6.default.createElement(
|
|
2586
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ import_react6.default.createElement(import_Skeleton3.default, { variant: "rounded", height: 80 })));
|
|
2503
2587
|
}
|
|
2504
2588
|
if (modelRows.length === 0) {
|
|
2505
|
-
return /* @__PURE__ */ import_react6.default.createElement(
|
|
2589
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { colSpan: 8 }, /* @__PURE__ */ import_react6.default.createElement(EmptyState, { message: "No model activity in this period" })));
|
|
2506
2590
|
}
|
|
2507
|
-
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2591
|
+
return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, { key: r.model }, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ import_react6.default.createElement(import_Box6.default, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ import_react6.default.createElement(import_Typography6.default, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, /* @__PURE__ */ import_react6.default.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
|
|
2508
2592
|
};
|
|
2509
2593
|
const periodSelect = /* @__PURE__ */ import_react6.default.createElement(
|
|
2510
2594
|
import_TextField4.default,
|
|
@@ -2657,7 +2741,7 @@ var init_UsageStats = __esm({
|
|
|
2657
2741
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2658
2742
|
},
|
|
2659
2743
|
/* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Legend, { ...chart.legend }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
|
|
2660
|
-
))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2744
|
+
))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Model"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Success"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Input"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Output"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody3.default, null, renderModelRows()))))),
|
|
2661
2745
|
tab === "keys" && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2662
2746
|
ChartOrFallback,
|
|
2663
2747
|
{
|
|
@@ -2666,7 +2750,7 @@ var init_UsageStats = __esm({
|
|
|
2666
2750
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2667
2751
|
},
|
|
2668
2752
|
/* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ import_react6.default.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
|
|
2669
|
-
))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(
|
|
2753
|
+
))), /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(import_TableContainer3.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ import_react6.default.createElement(import_Table3.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react6.default.createElement(import_TableHead3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableRow3.default, null, /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Key"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, null, "Models"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Spend"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Requests"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Tokens"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { align: "right" }, "Failed"), /* @__PURE__ */ import_react6.default.createElement(import_TableCell3.default, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ import_react6.default.createElement(import_TableBody3.default, null, renderKeyRows())))))
|
|
2670
2754
|
);
|
|
2671
2755
|
};
|
|
2672
2756
|
}
|
|
@@ -2678,26 +2762,26 @@ function budgetTone2(isOver, isNear) {
|
|
|
2678
2762
|
if (isNear) return "warning";
|
|
2679
2763
|
return "accent";
|
|
2680
2764
|
}
|
|
2681
|
-
var import_react7,
|
|
2765
|
+
var import_react7, import_Paper5, import_Box7, import_Typography7, import_Skeleton4, import_Divider3, import_Table4, import_TableBody4, import_TableCell4, import_TableHead4, import_TableRow4, import_TableContainer4, import_Collapse, import_IconButton4, import_Button5, import_CircularProgress4, import_styles4, import_icons_material5, import_recharts2, fmtUsd2, TeamCard, TeamUsage;
|
|
2682
2766
|
var init_TeamUsage = __esm({
|
|
2683
2767
|
"src/components/TeamUsage.tsx"() {
|
|
2684
2768
|
"use strict";
|
|
2685
2769
|
import_react7 = __toESM(require("react"));
|
|
2686
|
-
|
|
2770
|
+
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2687
2771
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
2688
2772
|
import_Typography7 = __toESM(require("@mui/material/Typography"));
|
|
2689
2773
|
import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
|
|
2690
2774
|
import_Divider3 = __toESM(require("@mui/material/Divider"));
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2775
|
+
import_Table4 = __toESM(require("@mui/material/Table"));
|
|
2776
|
+
import_TableBody4 = __toESM(require("@mui/material/TableBody"));
|
|
2777
|
+
import_TableCell4 = __toESM(require("@mui/material/TableCell"));
|
|
2778
|
+
import_TableHead4 = __toESM(require("@mui/material/TableHead"));
|
|
2779
|
+
import_TableRow4 = __toESM(require("@mui/material/TableRow"));
|
|
2780
|
+
import_TableContainer4 = __toESM(require("@mui/material/TableContainer"));
|
|
2697
2781
|
import_Collapse = __toESM(require("@mui/material/Collapse"));
|
|
2698
2782
|
import_IconButton4 = __toESM(require("@mui/material/IconButton"));
|
|
2699
2783
|
import_Button5 = __toESM(require("@mui/material/Button"));
|
|
2700
|
-
|
|
2784
|
+
import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
|
|
2701
2785
|
import_styles4 = require("@mui/material/styles");
|
|
2702
2786
|
import_icons_material5 = require("@mui/icons-material");
|
|
2703
2787
|
import_recharts2 = require("recharts");
|
|
@@ -2714,7 +2798,7 @@ var init_TeamUsage = __esm({
|
|
|
2714
2798
|
const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
|
|
2715
2799
|
const renderDailySpendSection = () => {
|
|
2716
2800
|
if (usageLoading) {
|
|
2717
|
-
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2801
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ import_react7.default.createElement(import_CircularProgress4.default, { size: 22 }));
|
|
2718
2802
|
}
|
|
2719
2803
|
if (dailyData.length === 0) return null;
|
|
2720
2804
|
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { mb: 2.5 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
@@ -2753,7 +2837,7 @@ var init_TeamUsage = __esm({
|
|
|
2753
2837
|
},
|
|
2754
2838
|
label
|
|
2755
2839
|
);
|
|
2756
|
-
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2840
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Paper5.default, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2757
2841
|
import_Box7.default,
|
|
2758
2842
|
{
|
|
2759
2843
|
sx: (theme) => ({
|
|
@@ -2818,13 +2902,13 @@ var init_TeamUsage = __esm({
|
|
|
2818
2902
|
/* @__PURE__ */ import_react7.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
2819
2903
|
/* @__PURE__ */ import_react7.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
2820
2904
|
), budget > 0 && /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ import_react7.default.createElement(Meter, { value: budgetPct, tone: budgetTone2(isOver, isNear), height: 5 })), /* @__PURE__ */ import_react7.default.createElement(import_Collapse.default, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ import_react7.default.createElement(import_Divider3.default, { sx: { my: 2.5 } }), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ import_react7.default.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ import_react7.default.createElement(
|
|
2821
|
-
|
|
2905
|
+
import_TableContainer4.default,
|
|
2822
2906
|
{
|
|
2823
|
-
component:
|
|
2907
|
+
component: import_Paper5.default,
|
|
2824
2908
|
variant: "outlined",
|
|
2825
2909
|
sx: { borderRadius: 1.5 }
|
|
2826
2910
|
},
|
|
2827
|
-
/* @__PURE__ */ import_react7.default.createElement(
|
|
2911
|
+
/* @__PURE__ */ import_react7.default.createElement(import_Table4.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react7.default.createElement(import_TableHead4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, null, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, "User"), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, "Role"))), /* @__PURE__ */ import_react7.default.createElement(import_TableBody4.default, null, team.members_with_roles.map((m) => /* @__PURE__ */ import_react7.default.createElement(import_TableRow4.default, { key: m.user_id }, /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, null, m.user_email ? /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2" }, m.user_email), /* @__PURE__ */ import_react7.default.createElement(
|
|
2828
2912
|
import_Typography7.default,
|
|
2829
2913
|
{
|
|
2830
2914
|
variant: "caption",
|
|
@@ -2839,7 +2923,7 @@ var init_TeamUsage = __esm({
|
|
|
2839
2923
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2840
2924
|
},
|
|
2841
2925
|
m.user_id
|
|
2842
|
-
)), /* @__PURE__ */ import_react7.default.createElement(
|
|
2926
|
+
)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2843
2927
|
StatusPill,
|
|
2844
2928
|
{
|
|
2845
2929
|
label: m.role,
|
|
@@ -2855,13 +2939,26 @@ var init_TeamUsage = __esm({
|
|
|
2855
2939
|
getTeamUsage,
|
|
2856
2940
|
getTeamUsageLoading,
|
|
2857
2941
|
canManage,
|
|
2858
|
-
onEditTeam
|
|
2942
|
+
onEditTeam,
|
|
2943
|
+
canCreate,
|
|
2944
|
+
onCreateTeam
|
|
2859
2945
|
}) => {
|
|
2946
|
+
const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ import_react7.default.createElement(
|
|
2947
|
+
import_Button5.default,
|
|
2948
|
+
{
|
|
2949
|
+
variant: "contained",
|
|
2950
|
+
color: "primary",
|
|
2951
|
+
disableElevation: true,
|
|
2952
|
+
startIcon: /* @__PURE__ */ import_react7.default.createElement(import_icons_material5.Add, null),
|
|
2953
|
+
onClick: onCreateTeam
|
|
2954
|
+
},
|
|
2955
|
+
"Create Team"
|
|
2956
|
+
) : void 0;
|
|
2860
2957
|
if (loading) {
|
|
2861
|
-
return /* @__PURE__ */ import_react7.default.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react7.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
|
|
2958
|
+
return /* @__PURE__ */ import_react7.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react7.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ import_react7.default.createElement(import_Skeleton4.default, { variant: "rounded", height: 120 }));
|
|
2862
2959
|
}
|
|
2863
2960
|
if (!teams.length) {
|
|
2864
|
-
return /* @__PURE__ */ import_react7.default.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2961
|
+
return /* @__PURE__ */ import_react7.default.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2865
2962
|
EmptyState,
|
|
2866
2963
|
{
|
|
2867
2964
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -2869,17 +2966,25 @@ var init_TeamUsage = __esm({
|
|
|
2869
2966
|
}
|
|
2870
2967
|
));
|
|
2871
2968
|
}
|
|
2872
|
-
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2873
|
-
|
|
2969
|
+
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2970
|
+
SectionCard,
|
|
2874
2971
|
{
|
|
2875
|
-
|
|
2876
|
-
team
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2972
|
+
title: "Teams",
|
|
2973
|
+
subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
|
|
2974
|
+
actions: createAction
|
|
2975
|
+
},
|
|
2976
|
+
/* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ import_react7.default.createElement(
|
|
2977
|
+
TeamCard,
|
|
2978
|
+
{
|
|
2979
|
+
key: team.team_id,
|
|
2980
|
+
team,
|
|
2981
|
+
usage: getTeamUsage(team.team_id),
|
|
2982
|
+
usageLoading: getTeamUsageLoading(team.team_id),
|
|
2983
|
+
canManage,
|
|
2984
|
+
onEditTeam
|
|
2985
|
+
}
|
|
2986
|
+
)))
|
|
2987
|
+
);
|
|
2883
2988
|
};
|
|
2884
2989
|
}
|
|
2885
2990
|
});
|
|
@@ -2902,18 +3007,18 @@ function fmtTokens(n) {
|
|
|
2902
3007
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2903
3008
|
return String(n);
|
|
2904
3009
|
}
|
|
2905
|
-
var import_react8, import_Box8,
|
|
3010
|
+
var import_react8, import_Box8, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_Typography8, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert3, import_styles5, ALL_PROXY_MODELS2, ModelsTable;
|
|
2906
3011
|
var init_ModelsTable = __esm({
|
|
2907
3012
|
"src/components/ModelsTable.tsx"() {
|
|
2908
3013
|
"use strict";
|
|
2909
3014
|
import_react8 = __toESM(require("react"));
|
|
2910
3015
|
import_Box8 = __toESM(require("@mui/material/Box"));
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3016
|
+
import_Table5 = __toESM(require("@mui/material/Table"));
|
|
3017
|
+
import_TableBody5 = __toESM(require("@mui/material/TableBody"));
|
|
3018
|
+
import_TableCell5 = __toESM(require("@mui/material/TableCell"));
|
|
3019
|
+
import_TableContainer5 = __toESM(require("@mui/material/TableContainer"));
|
|
3020
|
+
import_TableHead5 = __toESM(require("@mui/material/TableHead"));
|
|
3021
|
+
import_TableRow5 = __toESM(require("@mui/material/TableRow"));
|
|
2917
3022
|
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
2918
3023
|
import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
|
|
2919
3024
|
import_Select = __toESM(require("@mui/material/Select"));
|
|
@@ -2986,7 +3091,7 @@ var init_ModelsTable = __esm({
|
|
|
2986
3091
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
2987
3092
|
}
|
|
2988
3093
|
),
|
|
2989
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react8.default.createElement(
|
|
3094
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react8.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react8.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Model ID"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Mode"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, "Input Cost"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, "Output Cost"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, "Max Input"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, "Max Output"), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react8.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react8.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react8.default.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ import_react8.default.createElement(import_Tooltip.default, { title: "Copy model ID" }, /* @__PURE__ */ import_react8.default.createElement(
|
|
2990
3095
|
import_Box8.default,
|
|
2991
3096
|
{
|
|
2992
3097
|
component: "span",
|
|
@@ -2998,7 +3103,7 @@ var init_ModelsTable = __esm({
|
|
|
2998
3103
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
2999
3104
|
},
|
|
3000
3105
|
/* @__PURE__ */ import_react8.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
|
|
3001
|
-
)))), /* @__PURE__ */ import_react8.default.createElement(
|
|
3106
|
+
)))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
3002
3107
|
import_Box8.default,
|
|
3003
3108
|
{
|
|
3004
3109
|
component: "span",
|
|
@@ -3015,7 +3120,7 @@ var init_ModelsTable = __esm({
|
|
|
3015
3120
|
}
|
|
3016
3121
|
},
|
|
3017
3122
|
m.mode
|
|
3018
|
-
)), /* @__PURE__ */ import_react8.default.createElement(
|
|
3123
|
+
)), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, { align: "right" }, /* @__PURE__ */ import_react8.default.createElement(import_Typography8.default, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(import_Box8.default, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ import_react8.default.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ import_react8.default.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
|
|
3019
3124
|
/* @__PURE__ */ import_react8.default.createElement(
|
|
3020
3125
|
import_Snackbar.default,
|
|
3021
3126
|
{
|
|
@@ -3052,10 +3157,10 @@ function formatDateTime(iso) {
|
|
|
3052
3157
|
}
|
|
3053
3158
|
function renderAuditLogBody(loading, entries) {
|
|
3054
3159
|
if (loading) {
|
|
3055
|
-
return /* @__PURE__ */ import_react9.default.createElement(
|
|
3160
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ import_react9.default.createElement(import_Skeleton5.default, { variant: "rounded", height: 160 })));
|
|
3056
3161
|
}
|
|
3057
3162
|
if (entries.length === 0) {
|
|
3058
|
-
return /* @__PURE__ */ import_react9.default.createElement(
|
|
3163
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { colSpan: 6 }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3059
3164
|
EmptyState,
|
|
3060
3165
|
{
|
|
3061
3166
|
message: "No audit events found",
|
|
@@ -3065,19 +3170,19 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3065
3170
|
}
|
|
3066
3171
|
return entries.map((entry) => /* @__PURE__ */ import_react9.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3067
3172
|
}
|
|
3068
|
-
var import_react9, import_Box9, import_Typography9,
|
|
3173
|
+
var import_react9, import_Box9, import_Typography9, import_Table6, import_TableBody6, import_TableCell6, import_TableContainer6, import_TableHead6, import_TableRow6, import_TablePagination, import_TextField5, import_MenuItem5, import_Skeleton5, import_Collapse2, import_IconButton5, import_Alert4, import_styles6, import_icons_material6, import_react_use2, ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
|
|
3069
3174
|
var init_AuditLog = __esm({
|
|
3070
3175
|
"src/components/AuditLog.tsx"() {
|
|
3071
3176
|
"use strict";
|
|
3072
3177
|
import_react9 = __toESM(require("react"));
|
|
3073
3178
|
import_Box9 = __toESM(require("@mui/material/Box"));
|
|
3074
3179
|
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3180
|
+
import_Table6 = __toESM(require("@mui/material/Table"));
|
|
3181
|
+
import_TableBody6 = __toESM(require("@mui/material/TableBody"));
|
|
3182
|
+
import_TableCell6 = __toESM(require("@mui/material/TableCell"));
|
|
3183
|
+
import_TableContainer6 = __toESM(require("@mui/material/TableContainer"));
|
|
3184
|
+
import_TableHead6 = __toESM(require("@mui/material/TableHead"));
|
|
3185
|
+
import_TableRow6 = __toESM(require("@mui/material/TableRow"));
|
|
3081
3186
|
import_TablePagination = __toESM(require("@mui/material/TablePagination"));
|
|
3082
3187
|
import_TextField5 = __toESM(require("@mui/material/TextField"));
|
|
3083
3188
|
import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
|
|
@@ -3087,7 +3192,7 @@ var init_AuditLog = __esm({
|
|
|
3087
3192
|
import_Alert4 = __toESM(require("@mui/material/Alert"));
|
|
3088
3193
|
import_styles6 = require("@mui/material/styles");
|
|
3089
3194
|
import_icons_material6 = require("@mui/icons-material");
|
|
3090
|
-
|
|
3195
|
+
import_react_use2 = require("react-use");
|
|
3091
3196
|
init_ui();
|
|
3092
3197
|
ACTION_TONES = {
|
|
3093
3198
|
created: "success",
|
|
@@ -3106,7 +3211,7 @@ var init_AuditLog = __esm({
|
|
|
3106
3211
|
DetailRow = ({ entry }) => {
|
|
3107
3212
|
const [open, setOpen] = (0, import_react9.useState)(false);
|
|
3108
3213
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3109
|
-
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(
|
|
3214
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react9.default.Fragment, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ import_react9.default.createElement(
|
|
3110
3215
|
import_IconButton5.default,
|
|
3111
3216
|
{
|
|
3112
3217
|
size: "small",
|
|
@@ -3120,7 +3225,7 @@ var init_AuditLog = __esm({
|
|
|
3120
3225
|
})
|
|
3121
3226
|
},
|
|
3122
3227
|
/* @__PURE__ */ import_react9.default.createElement(import_icons_material6.KeyboardArrowDown, { fontSize: "small" })
|
|
3123
|
-
)), /* @__PURE__ */ import_react9.default.createElement(
|
|
3228
|
+
)), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, entry.action && /* @__PURE__ */ import_react9.default.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Typography9.default, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, /* @__PURE__ */ import_react9.default.createElement(
|
|
3124
3229
|
import_Typography9.default,
|
|
3125
3230
|
{
|
|
3126
3231
|
variant: "body2",
|
|
@@ -3130,7 +3235,7 @@ var init_AuditLog = __esm({
|
|
|
3130
3235
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3131
3236
|
},
|
|
3132
3237
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3133
|
-
)), /* @__PURE__ */ import_react9.default.createElement(
|
|
3238
|
+
)), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ import_react9.default.createElement(import_TableRow6.default, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ import_react9.default.createElement(import_Collapse2.default, { in: open, unmountOnExit: true }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3134
3239
|
import_Box9.default,
|
|
3135
3240
|
{
|
|
3136
3241
|
display: "flex",
|
|
@@ -3175,7 +3280,7 @@ var init_AuditLog = __esm({
|
|
|
3175
3280
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3176
3281
|
[page, pageSize, filters]
|
|
3177
3282
|
)();
|
|
3178
|
-
const { value, loading, error } = (0,
|
|
3283
|
+
const { value, loading, error } = (0, import_react_use2.useAsync)(
|
|
3179
3284
|
() => api.getAuditLogs(fetchParams),
|
|
3180
3285
|
[api, fetchParams]
|
|
3181
3286
|
);
|
|
@@ -3239,7 +3344,7 @@ var init_AuditLog = __esm({
|
|
|
3239
3344
|
))
|
|
3240
3345
|
},
|
|
3241
3346
|
error && /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { px: 2.5, pb: 2 }, /* @__PURE__ */ import_react9.default.createElement(import_Alert4.default, { severity: "error" }, error.message)),
|
|
3242
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3347
|
+
/* @__PURE__ */ import_react9.default.createElement(import_TableContainer6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Table6.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow6.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, { sx: { width: 40 } }), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, "Time"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, "Action"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, "Table"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, "Object ID"), /* @__PURE__ */ import_react9.default.createElement(import_TableCell6.default, null, "Changed By"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody6.default, null, renderAuditLogBody(loading, entries)))),
|
|
3243
3348
|
/* @__PURE__ */ import_react9.default.createElement(
|
|
3244
3349
|
import_TablePagination.default,
|
|
3245
3350
|
{
|
|
@@ -3309,7 +3414,7 @@ function initDateRange() {
|
|
|
3309
3414
|
else start.setDate(start.getDate() - 7);
|
|
3310
3415
|
return { start, end };
|
|
3311
3416
|
}
|
|
3312
|
-
var import_react10, import_Box10, import_Snackbar2, import_Alert5,
|
|
3417
|
+
var import_react10, import_Box10, import_Snackbar2, import_Alert5, import_CircularProgress5, import_Typography10, import_Paper6, import_Tabs2, import_Tab2, import_react_use3, import_core_plugin_api3, import_plugin_permission_react, PERIOD_LS_KEY2, LiteLLMPage;
|
|
3313
3418
|
var init_LiteLLMPage = __esm({
|
|
3314
3419
|
"src/components/LiteLLMPage.tsx"() {
|
|
3315
3420
|
"use strict";
|
|
@@ -3317,14 +3422,13 @@ var init_LiteLLMPage = __esm({
|
|
|
3317
3422
|
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3318
3423
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
3319
3424
|
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
3320
|
-
|
|
3425
|
+
import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
|
|
3321
3426
|
import_Typography10 = __toESM(require("@mui/material/Typography"));
|
|
3322
|
-
|
|
3427
|
+
import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
3323
3428
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
3324
3429
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
import_core_plugin_api2 = require("@backstage/core-plugin-api");
|
|
3430
|
+
import_react_use3 = require("react-use");
|
|
3431
|
+
import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
3328
3432
|
import_plugin_permission_react = require("@backstage/plugin-permission-react");
|
|
3329
3433
|
init_DashboardHeader();
|
|
3330
3434
|
init_KeysTable();
|
|
@@ -3338,7 +3442,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3338
3442
|
init_permissions();
|
|
3339
3443
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
3340
3444
|
LiteLLMPage = () => {
|
|
3341
|
-
const api = (0,
|
|
3445
|
+
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3342
3446
|
const [dateRange, setDateRange] = (0, import_react10.useState)(initDateRange);
|
|
3343
3447
|
const [currentPreset, setCurrentPreset] = (0, import_react10.useState)(() => {
|
|
3344
3448
|
try {
|
|
@@ -3353,11 +3457,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3353
3457
|
const [manageTeam, setManageTeam] = (0, import_react10.useState)(null);
|
|
3354
3458
|
const [teamUsageCache, setTeamUsageCache] = (0, import_react10.useState)({});
|
|
3355
3459
|
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react10.useState)({});
|
|
3356
|
-
const { value: userInfo, loading: userLoading, error: userError } = (0,
|
|
3460
|
+
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
3357
3461
|
() => api.getUserInfo(),
|
|
3358
3462
|
[api]
|
|
3359
3463
|
);
|
|
3360
|
-
const { value: keys, loading: keysLoading, retry: refreshKeys } = (0,
|
|
3464
|
+
const { value: keys, loading: keysLoading, retry: refreshKeys } = (0, import_react_use3.useAsyncRetry)(
|
|
3361
3465
|
async () => {
|
|
3362
3466
|
try {
|
|
3363
3467
|
return await api.listKeys();
|
|
@@ -3368,16 +3472,16 @@ var init_LiteLLMPage = __esm({
|
|
|
3368
3472
|
},
|
|
3369
3473
|
[api]
|
|
3370
3474
|
);
|
|
3371
|
-
const { value: allModels, loading: modelsLoading } = (0,
|
|
3475
|
+
const { value: allModels, loading: modelsLoading } = (0, import_react_use3.useAsync)(
|
|
3372
3476
|
() => api.listModels().catch(() => []),
|
|
3373
3477
|
[api]
|
|
3374
3478
|
);
|
|
3375
|
-
const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = (0,
|
|
3479
|
+
const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = (0, import_react_use3.useAsyncRetry)(
|
|
3376
3480
|
() => api.getTeams().catch(() => []),
|
|
3377
3481
|
[api]
|
|
3378
3482
|
);
|
|
3379
|
-
const { value: liteLlmConfig } = (0,
|
|
3380
|
-
const { value: managedTeams } = (0,
|
|
3483
|
+
const { value: liteLlmConfig } = (0, import_react_use3.useAsync)(() => api.getConfig(), [api]);
|
|
3484
|
+
const { value: managedTeams } = (0, import_react_use3.useAsync)(
|
|
3381
3485
|
async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
|
|
3382
3486
|
[api, liteLlmConfig]
|
|
3383
3487
|
);
|
|
@@ -3388,11 +3492,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3388
3492
|
const { allowed: canManageMcpServers } = (0, import_plugin_permission_react.usePermission)({ permission: litellmTeamMcpManagePermission });
|
|
3389
3493
|
const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
|
|
3390
3494
|
const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
|
|
3391
|
-
const { value: vectorStores } = (0,
|
|
3495
|
+
const { value: vectorStores } = (0, import_react_use3.useAsync)(
|
|
3392
3496
|
async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
|
|
3393
3497
|
[api, objectPermsEnabled]
|
|
3394
3498
|
);
|
|
3395
|
-
const { value: mcpServers } = (0,
|
|
3499
|
+
const { value: mcpServers } = (0, import_react_use3.useAsync)(
|
|
3396
3500
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
3397
3501
|
[api, objectPermsEnabled]
|
|
3398
3502
|
);
|
|
@@ -3408,7 +3512,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3408
3512
|
);
|
|
3409
3513
|
return byMembership.length > 0 ? byMembership : allTeams;
|
|
3410
3514
|
}, [allTeams, userInfo]);
|
|
3411
|
-
const { value: usage, loading: usageLoading } = (0,
|
|
3515
|
+
const { value: usage, loading: usageLoading } = (0, import_react_use3.useAsync)(async () => {
|
|
3412
3516
|
const startDate = dateRange.start.toISOString().split("T")[0];
|
|
3413
3517
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
3414
3518
|
return api.getUsage(startDate, endDate);
|
|
@@ -3543,12 +3647,12 @@ var init_LiteLLMPage = __esm({
|
|
|
3543
3647
|
);
|
|
3544
3648
|
const isInitialLoading = userLoading && !userInfo;
|
|
3545
3649
|
if (isInitialLoading) {
|
|
3546
|
-
return /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3650
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ import_react10.default.createElement(import_CircularProgress5.default, null));
|
|
3547
3651
|
}
|
|
3548
3652
|
if (userError || !userInfo) {
|
|
3549
3653
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
3550
3654
|
const hint = userError?.body?.hint;
|
|
3551
|
-
return /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { p: 3 }, /* @__PURE__ */ import_react10.default.createElement(
|
|
3655
|
+
return /* @__PURE__ */ import_react10.default.createElement(import_Box10.default, { p: 3 }, /* @__PURE__ */ import_react10.default.createElement(import_Paper6.default, { sx: { p: 3 } }, /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ import_react10.default.createElement(import_Typography10.default, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
|
|
3552
3656
|
}
|
|
3553
3657
|
const pageTabs = /* @__PURE__ */ import_react10.default.createElement(
|
|
3554
3658
|
import_Tabs2.default,
|
|
@@ -3609,14 +3713,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3609
3713
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
3610
3714
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
3611
3715
|
const visibleTeams = Array.from(teamsById.values());
|
|
3612
|
-
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3613
|
-
import_Button6.default,
|
|
3614
|
-
{
|
|
3615
|
-
variant: "contained",
|
|
3616
|
-
onClick: () => setManageTeam({ mode: "create" })
|
|
3617
|
-
},
|
|
3618
|
-
"Create Team"
|
|
3619
|
-
)), /* @__PURE__ */ import_react10.default.createElement(
|
|
3716
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3620
3717
|
TeamUsage,
|
|
3621
3718
|
{
|
|
3622
3719
|
teams: visibleTeams,
|
|
@@ -3627,9 +3724,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3627
3724
|
},
|
|
3628
3725
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
3629
3726
|
canManage: teamMgmtEnabled && canManageTeam,
|
|
3630
|
-
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
|
|
3727
|
+
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
3728
|
+
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
3729
|
+
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
3631
3730
|
}
|
|
3632
|
-
)
|
|
3731
|
+
);
|
|
3633
3732
|
})(), activeTab === "models" && /* @__PURE__ */ import_react10.default.createElement(
|
|
3634
3733
|
ModelsTable,
|
|
3635
3734
|
{
|
|
@@ -3785,17 +3884,17 @@ init_TeamUsage();
|
|
|
3785
3884
|
|
|
3786
3885
|
// src/components/LiteLLMHomeWidget.tsx
|
|
3787
3886
|
var import_react12 = __toESM(require("react"));
|
|
3788
|
-
var
|
|
3887
|
+
var import_Paper7 = __toESM(require("@mui/material/Paper"));
|
|
3789
3888
|
var import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3790
3889
|
var import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3791
3890
|
var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
|
|
3792
3891
|
var import_Select2 = __toESM(require("@mui/material/Select"));
|
|
3793
3892
|
var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
|
|
3794
3893
|
var import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
3795
|
-
var
|
|
3894
|
+
var import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
|
|
3796
3895
|
var import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
3797
3896
|
var import_recharts3 = require("recharts");
|
|
3798
|
-
var
|
|
3897
|
+
var import_core_plugin_api4 = require("@backstage/core-plugin-api");
|
|
3799
3898
|
init_api();
|
|
3800
3899
|
init_format();
|
|
3801
3900
|
function presetToDateRange(preset) {
|
|
@@ -3815,7 +3914,7 @@ var LiteLLMHomeWidget = ({
|
|
|
3815
3914
|
defaultPeriod = "7d",
|
|
3816
3915
|
title = "LiteLLM Usage"
|
|
3817
3916
|
}) => {
|
|
3818
|
-
const api = (0,
|
|
3917
|
+
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
3819
3918
|
const [period, setPeriod] = (0, import_react12.useState)(defaultPeriod);
|
|
3820
3919
|
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
3821
3920
|
const [usageError, setUsageError] = (0, import_react12.useState)(null);
|
|
@@ -3859,7 +3958,7 @@ var LiteLLMHomeWidget = ({
|
|
|
3859
3958
|
spend: d.spend
|
|
3860
3959
|
}));
|
|
3861
3960
|
const hasSparkline = dailyData.length > 0;
|
|
3862
|
-
return /* @__PURE__ */ import_react12.default.createElement(
|
|
3961
|
+
return /* @__PURE__ */ import_react12.default.createElement(import_Paper7.default, { sx: { p: 2 } }, /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ import_react12.default.createElement(import_Typography11.default, { variant: "h6" }, title), /* @__PURE__ */ import_react12.default.createElement(import_FormControl2.default, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3863
3962
|
import_Select2.default,
|
|
3864
3963
|
{
|
|
3865
3964
|
value: period,
|
|
@@ -3869,7 +3968,7 @@ var LiteLLMHomeWidget = ({
|
|
|
3869
3968
|
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
|
|
3870
3969
|
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
|
|
3871
3970
|
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
|
|
3872
|
-
))), loading && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3971
|
+
))), loading && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react12.default.createElement(import_CircularProgress6.default, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, partialFailure && /* @__PURE__ */ import_react12.default.createElement(import_Alert6.default, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react12.default.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react12.default.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react12.default.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ import_react12.default.createElement(import_Grid2.default, { item: true, xs: 6 }, /* @__PURE__ */ import_react12.default.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { height: 120 }, /* @__PURE__ */ import_react12.default.createElement(import_recharts3.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react12.default.createElement(import_recharts3.AreaChart, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3873
3972
|
import_recharts3.Area,
|
|
3874
3973
|
{
|
|
3875
3974
|
type: "monotone",
|