@acarmisc/backstage-plugin-litellm 0.16.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 +282 -167
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.esm.js +292 -177
- package/dist/index.esm.js.map +3 -3
- package/dist/types.d.ts +3 -2
- 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_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,14 +1957,34 @@ 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"));
|
|
1961
|
+
import_MenuItem2 = __toESM(require("@mui/material/MenuItem"));
|
|
1960
1962
|
import_Box5 = __toESM(require("@mui/material/Box"));
|
|
1961
1963
|
import_Divider2 = __toESM(require("@mui/material/Divider"));
|
|
1962
1964
|
import_Typography5 = __toESM(require("@mui/material/Typography"));
|
|
1963
1965
|
import_IconButton3 = __toESM(require("@mui/material/IconButton"));
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
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"));
|
|
1967
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();
|
|
1979
|
+
BUDGET_DURATION_OPTIONS = [
|
|
1980
|
+
{ value: "1d", label: "Daily (1d)" },
|
|
1981
|
+
{ value: "7d", label: "Weekly (7d)" },
|
|
1982
|
+
{ value: "30d", label: "Monthly (30d)" },
|
|
1983
|
+
{ value: "90d", label: "Quarterly (90d)" },
|
|
1984
|
+
{ value: "365d", label: "Yearly (365d)" }
|
|
1985
|
+
];
|
|
1986
|
+
DEFAULT_BUDGET_DURATION = "30d";
|
|
1987
|
+
FALLBACK_BUDGET_CEILING = 1e3;
|
|
1968
1988
|
ManageTeamDialog = ({
|
|
1969
1989
|
open,
|
|
1970
1990
|
onClose,
|
|
@@ -1990,6 +2010,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
1990
2010
|
const [budgetDuration, setBudgetDuration] = (0, import_react5.useState)("");
|
|
1991
2011
|
const [submitting, setSubmitting] = (0, import_react5.useState)(false);
|
|
1992
2012
|
const [error, setError] = (0, import_react5.useState)(null);
|
|
2013
|
+
const [memberQuery, setMemberQuery] = (0, import_react5.useState)("");
|
|
1993
2014
|
const [memberRef, setMemberRef] = (0, import_react5.useState)("");
|
|
1994
2015
|
const [memberBudget, setMemberBudget] = (0, import_react5.useState)("");
|
|
1995
2016
|
const [memberBusy, setMemberBusy] = (0, import_react5.useState)(false);
|
|
@@ -2001,23 +2022,56 @@ var init_ManageTeamDialog = __esm({
|
|
|
2001
2022
|
const [mcpBusy, setMcpBusy] = (0, import_react5.useState)(false);
|
|
2002
2023
|
const [mcpError, setMcpError] = (0, import_react5.useState)(null);
|
|
2003
2024
|
const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
|
|
2004
|
-
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling;
|
|
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]);
|
|
2005
2056
|
(0, import_react5.useEffect)(() => {
|
|
2006
2057
|
if (open) {
|
|
2007
2058
|
if (mode === "edit" && team) {
|
|
2008
2059
|
setAlias(team.team_alias ?? "");
|
|
2009
2060
|
setModels(team.models ?? []);
|
|
2010
|
-
setMaxBudget(
|
|
2061
|
+
setMaxBudget(
|
|
2062
|
+
team.max_budget ? String(team.max_budget) : String(maxBudgetCeiling)
|
|
2063
|
+
);
|
|
2011
2064
|
setUnlimited(allowUnlimitedBudget && !team.max_budget);
|
|
2012
|
-
setBudgetDuration(
|
|
2065
|
+
setBudgetDuration(team.budget_duration ?? DEFAULT_BUDGET_DURATION);
|
|
2013
2066
|
} else {
|
|
2014
2067
|
setAlias("");
|
|
2015
2068
|
setModels([]);
|
|
2016
|
-
setMaxBudget(
|
|
2069
|
+
setMaxBudget(String(maxBudgetCeiling));
|
|
2017
2070
|
setUnlimited(allowUnlimitedBudget);
|
|
2018
|
-
setBudgetDuration(
|
|
2071
|
+
setBudgetDuration(DEFAULT_BUDGET_DURATION);
|
|
2019
2072
|
}
|
|
2020
2073
|
setError(null);
|
|
2074
|
+
setMemberQuery("");
|
|
2021
2075
|
setMemberRef("");
|
|
2022
2076
|
setMemberBudget("");
|
|
2023
2077
|
setMemberError(null);
|
|
@@ -2026,7 +2080,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2026
2080
|
setMcpIds(team?.object_permission?.mcp_servers ?? []);
|
|
2027
2081
|
setMcpError(null);
|
|
2028
2082
|
}
|
|
2029
|
-
}, [open, mode, team, allowUnlimitedBudget]);
|
|
2083
|
+
}, [open, mode, team, allowUnlimitedBudget, maxBudgetCeiling]);
|
|
2030
2084
|
const handleSubmit = async () => {
|
|
2031
2085
|
setError(null);
|
|
2032
2086
|
if (!alias.trim()) {
|
|
@@ -2047,7 +2101,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2047
2101
|
setError("Budget must be a positive number");
|
|
2048
2102
|
return;
|
|
2049
2103
|
}
|
|
2050
|
-
if (
|
|
2104
|
+
if (budget > maxBudgetCeiling) {
|
|
2051
2105
|
setError(`Budget cannot exceed $${maxBudgetCeiling}`);
|
|
2052
2106
|
return;
|
|
2053
2107
|
}
|
|
@@ -2089,6 +2143,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2089
2143
|
try {
|
|
2090
2144
|
setMemberBusy(true);
|
|
2091
2145
|
await onAddMember(memberRef.trim(), budget);
|
|
2146
|
+
setMemberQuery("");
|
|
2092
2147
|
setMemberRef("");
|
|
2093
2148
|
setMemberBudget("");
|
|
2094
2149
|
} catch (err) {
|
|
@@ -2134,8 +2189,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
2134
2189
|
}
|
|
2135
2190
|
};
|
|
2136
2191
|
const modelNames = allModels.map((m) => m.model_name);
|
|
2137
|
-
const
|
|
2138
|
-
const showMembers = mode === "edit" && !!canManageMembers;
|
|
2192
|
+
const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
|
|
2139
2193
|
const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
|
|
2140
2194
|
const kbOptions = (vectorStores ?? []).map((v) => v.id);
|
|
2141
2195
|
const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
|
|
@@ -2159,7 +2213,15 @@ var init_ManageTeamDialog = __esm({
|
|
|
2159
2213
|
value: models,
|
|
2160
2214
|
onChange: (_, newValue) => setModels(newValue),
|
|
2161
2215
|
disabled: submitting,
|
|
2162
|
-
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
|
+
)
|
|
2163
2225
|
}
|
|
2164
2226
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2165
2227
|
import_TextField3.default,
|
|
@@ -2169,7 +2231,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
2169
2231
|
value: maxBudget,
|
|
2170
2232
|
onChange: (e) => setMaxBudget(e.target.value),
|
|
2171
2233
|
disabled: submitting || unlimited,
|
|
2172
|
-
|
|
2234
|
+
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
2235
|
+
helperText: `Maximum: $${maxBudgetCeiling}`,
|
|
2173
2236
|
fullWidth: true
|
|
2174
2237
|
}
|
|
2175
2238
|
), allowUnlimitedBudget && /* @__PURE__ */ import_react5.default.createElement(
|
|
@@ -2181,41 +2244,78 @@ var init_ManageTeamDialog = __esm({
|
|
|
2181
2244
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2182
2245
|
import_TextField3.default,
|
|
2183
2246
|
{
|
|
2247
|
+
select: true,
|
|
2184
2248
|
label: "Budget Duration",
|
|
2185
2249
|
value: budgetDuration,
|
|
2186
2250
|
onChange: (e) => setBudgetDuration(e.target.value),
|
|
2187
|
-
|
|
2188
|
-
|
|
2251
|
+
disabled: submitting || unlimited,
|
|
2252
|
+
helperText: "Spend-reset period for the team budget",
|
|
2189
2253
|
fullWidth: true
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2254
|
+
},
|
|
2255
|
+
durationOptions.map((o) => /* @__PURE__ */ import_react5.default.createElement(import_MenuItem2.default, { key: o.value, value: o.value }, o.label))
|
|
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,
|
|
2193
2258
|
{
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
import_IconButton3.default,
|
|
2198
|
-
{
|
|
2199
|
-
edge: "end",
|
|
2200
|
-
size: "small",
|
|
2201
|
-
"aria-label": `remove ${m.user_id}`,
|
|
2202
|
-
disabled: memberBusy,
|
|
2203
|
-
onClick: () => handleRemoveMember(m.user_id)
|
|
2204
|
-
},
|
|
2205
|
-
/* @__PURE__ */ import_react5.default.createElement(import_icons_material4.Delete, { fontSize: "small" })
|
|
2206
|
-
)
|
|
2259
|
+
variant: "caption",
|
|
2260
|
+
color: "text.secondary",
|
|
2261
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
|
|
2207
2262
|
},
|
|
2208
|
-
|
|
2209
|
-
))
|
|
2210
|
-
|
|
2263
|
+
m.user_id
|
|
2264
|
+
)) : /* @__PURE__ */ import_react5.default.createElement(
|
|
2265
|
+
import_Typography5.default,
|
|
2211
2266
|
{
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
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",
|
|
2217
2275
|
size: "small",
|
|
2218
|
-
|
|
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
|
+
)
|
|
2219
2319
|
}
|
|
2220
2320
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
2221
2321
|
import_TextField3.default,
|
|
@@ -2286,23 +2386,23 @@ function rateTone(rate) {
|
|
|
2286
2386
|
if (rate >= 0.9) return "warning";
|
|
2287
2387
|
return "danger";
|
|
2288
2388
|
}
|
|
2289
|
-
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;
|
|
2290
2390
|
var init_UsageStats = __esm({
|
|
2291
2391
|
"src/components/UsageStats.tsx"() {
|
|
2292
2392
|
"use strict";
|
|
2293
2393
|
import_react6 = __toESM(require("react"));
|
|
2294
|
-
|
|
2394
|
+
import_Paper4 = __toESM(require("@mui/material/Paper"));
|
|
2295
2395
|
import_Box6 = __toESM(require("@mui/material/Box"));
|
|
2296
2396
|
import_Typography6 = __toESM(require("@mui/material/Typography"));
|
|
2297
2397
|
import_TextField4 = __toESM(require("@mui/material/TextField"));
|
|
2298
|
-
|
|
2398
|
+
import_MenuItem3 = __toESM(require("@mui/material/MenuItem"));
|
|
2299
2399
|
import_Grid = __toESM(require("@mui/material/Grid"));
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
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"));
|
|
2306
2406
|
import_Skeleton3 = __toESM(require("@mui/material/Skeleton"));
|
|
2307
2407
|
import_recharts = require("recharts");
|
|
2308
2408
|
init_format();
|
|
@@ -2474,21 +2574,21 @@ var init_UsageStats = __esm({
|
|
|
2474
2574
|
];
|
|
2475
2575
|
const renderKeyRows = () => {
|
|
2476
2576
|
if (loading) {
|
|
2477
|
-
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 })));
|
|
2478
2578
|
}
|
|
2479
2579
|
if (keyRows.length === 0) {
|
|
2480
|
-
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" })));
|
|
2481
2581
|
}
|
|
2482
|
-
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 }))));
|
|
2483
2583
|
};
|
|
2484
2584
|
const renderModelRows = () => {
|
|
2485
2585
|
if (loading) {
|
|
2486
|
-
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 })));
|
|
2487
2587
|
}
|
|
2488
2588
|
if (modelRows.length === 0) {
|
|
2489
|
-
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" })));
|
|
2490
2590
|
}
|
|
2491
|
-
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 }))));
|
|
2492
2592
|
};
|
|
2493
2593
|
const periodSelect = /* @__PURE__ */ import_react6.default.createElement(
|
|
2494
2594
|
import_TextField4.default,
|
|
@@ -2500,7 +2600,7 @@ var init_UsageStats = __esm({
|
|
|
2500
2600
|
onChange: (e) => handlePresetChange(e.target.value),
|
|
2501
2601
|
sx: { minWidth: 160 }
|
|
2502
2602
|
},
|
|
2503
|
-
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2603
|
+
Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { key: p, value: p }, PRESET_LABELS[p]))
|
|
2504
2604
|
);
|
|
2505
2605
|
return /* @__PURE__ */ import_react6.default.createElement(
|
|
2506
2606
|
SectionCard,
|
|
@@ -2545,8 +2645,8 @@ var init_UsageStats = __esm({
|
|
|
2545
2645
|
onChange: (e) => setSelectedModel(e.target.value),
|
|
2546
2646
|
sx: { minWidth: 220 }
|
|
2547
2647
|
},
|
|
2548
|
-
/* @__PURE__ */ import_react6.default.createElement(
|
|
2549
|
-
models.map((m) => /* @__PURE__ */ import_react6.default.createElement(
|
|
2648
|
+
/* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { value: "all" }, "All models"),
|
|
2649
|
+
models.map((m) => /* @__PURE__ */ import_react6.default.createElement(import_MenuItem3.default, { key: m.model_name, value: m.model_name }, m.model_name))
|
|
2550
2650
|
)
|
|
2551
2651
|
),
|
|
2552
2652
|
tab === "costs" && /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { container: true, spacing: 2 }, /* @__PURE__ */ import_react6.default.createElement(import_Grid.default, { item: true, xs: 12 }, /* @__PURE__ */ import_react6.default.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ import_react6.default.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ import_react6.default.createElement(import_recharts.CartesianGrid, { ...chart.grid }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(import_recharts.YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ import_react6.default.createElement(
|
|
@@ -2641,7 +2741,7 @@ var init_UsageStats = __esm({
|
|
|
2641
2741
|
height: Math.max(200, topModelSpendBars.length * 34)
|
|
2642
2742
|
},
|
|
2643
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] })))
|
|
2644
|
-
))), /* @__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()))))),
|
|
2645
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(
|
|
2646
2746
|
ChartOrFallback,
|
|
2647
2747
|
{
|
|
@@ -2650,7 +2750,7 @@ var init_UsageStats = __esm({
|
|
|
2650
2750
|
height: Math.max(160, topKeySpendBars.length * 32)
|
|
2651
2751
|
},
|
|
2652
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 })))
|
|
2653
|
-
))), /* @__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())))))
|
|
2654
2754
|
);
|
|
2655
2755
|
};
|
|
2656
2756
|
}
|
|
@@ -2662,26 +2762,26 @@ function budgetTone2(isOver, isNear) {
|
|
|
2662
2762
|
if (isNear) return "warning";
|
|
2663
2763
|
return "accent";
|
|
2664
2764
|
}
|
|
2665
|
-
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;
|
|
2666
2766
|
var init_TeamUsage = __esm({
|
|
2667
2767
|
"src/components/TeamUsage.tsx"() {
|
|
2668
2768
|
"use strict";
|
|
2669
2769
|
import_react7 = __toESM(require("react"));
|
|
2670
|
-
|
|
2770
|
+
import_Paper5 = __toESM(require("@mui/material/Paper"));
|
|
2671
2771
|
import_Box7 = __toESM(require("@mui/material/Box"));
|
|
2672
2772
|
import_Typography7 = __toESM(require("@mui/material/Typography"));
|
|
2673
2773
|
import_Skeleton4 = __toESM(require("@mui/material/Skeleton"));
|
|
2674
2774
|
import_Divider3 = __toESM(require("@mui/material/Divider"));
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
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"));
|
|
2681
2781
|
import_Collapse = __toESM(require("@mui/material/Collapse"));
|
|
2682
2782
|
import_IconButton4 = __toESM(require("@mui/material/IconButton"));
|
|
2683
2783
|
import_Button5 = __toESM(require("@mui/material/Button"));
|
|
2684
|
-
|
|
2784
|
+
import_CircularProgress4 = __toESM(require("@mui/material/CircularProgress"));
|
|
2685
2785
|
import_styles4 = require("@mui/material/styles");
|
|
2686
2786
|
import_icons_material5 = require("@mui/icons-material");
|
|
2687
2787
|
import_recharts2 = require("recharts");
|
|
@@ -2698,7 +2798,7 @@ var init_TeamUsage = __esm({
|
|
|
2698
2798
|
const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
|
|
2699
2799
|
const renderDailySpendSection = () => {
|
|
2700
2800
|
if (usageLoading) {
|
|
2701
|
-
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 }));
|
|
2702
2802
|
}
|
|
2703
2803
|
if (dailyData.length === 0) return null;
|
|
2704
2804
|
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { mb: 2.5 }, /* @__PURE__ */ import_react7.default.createElement(
|
|
@@ -2737,7 +2837,7 @@ var init_TeamUsage = __esm({
|
|
|
2737
2837
|
},
|
|
2738
2838
|
label
|
|
2739
2839
|
);
|
|
2740
|
-
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(
|
|
2741
2841
|
import_Box7.default,
|
|
2742
2842
|
{
|
|
2743
2843
|
sx: (theme) => ({
|
|
@@ -2802,13 +2902,13 @@ var init_TeamUsage = __esm({
|
|
|
2802
2902
|
/* @__PURE__ */ import_react7.default.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
|
|
2803
2903
|
/* @__PURE__ */ import_react7.default.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
|
|
2804
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(
|
|
2805
|
-
|
|
2905
|
+
import_TableContainer4.default,
|
|
2806
2906
|
{
|
|
2807
|
-
component:
|
|
2907
|
+
component: import_Paper5.default,
|
|
2808
2908
|
variant: "outlined",
|
|
2809
2909
|
sx: { borderRadius: 1.5 }
|
|
2810
2910
|
},
|
|
2811
|
-
/* @__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(
|
|
2812
2912
|
import_Typography7.default,
|
|
2813
2913
|
{
|
|
2814
2914
|
variant: "caption",
|
|
@@ -2823,7 +2923,7 @@ var init_TeamUsage = __esm({
|
|
|
2823
2923
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
2824
2924
|
},
|
|
2825
2925
|
m.user_id
|
|
2826
|
-
)), /* @__PURE__ */ import_react7.default.createElement(
|
|
2926
|
+
)), /* @__PURE__ */ import_react7.default.createElement(import_TableCell4.default, { align: "right" }, /* @__PURE__ */ import_react7.default.createElement(
|
|
2827
2927
|
StatusPill,
|
|
2828
2928
|
{
|
|
2829
2929
|
label: m.role,
|
|
@@ -2839,13 +2939,26 @@ var init_TeamUsage = __esm({
|
|
|
2839
2939
|
getTeamUsage,
|
|
2840
2940
|
getTeamUsageLoading,
|
|
2841
2941
|
canManage,
|
|
2842
|
-
onEditTeam
|
|
2942
|
+
onEditTeam,
|
|
2943
|
+
canCreate,
|
|
2944
|
+
onCreateTeam
|
|
2843
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;
|
|
2844
2957
|
if (loading) {
|
|
2845
|
-
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 }));
|
|
2846
2959
|
}
|
|
2847
2960
|
if (!teams.length) {
|
|
2848
|
-
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(
|
|
2849
2962
|
EmptyState,
|
|
2850
2963
|
{
|
|
2851
2964
|
message: "You're not a member of any LiteLLM team yet.",
|
|
@@ -2853,17 +2966,25 @@ var init_TeamUsage = __esm({
|
|
|
2853
2966
|
}
|
|
2854
2967
|
));
|
|
2855
2968
|
}
|
|
2856
|
-
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2857
|
-
|
|
2969
|
+
return /* @__PURE__ */ import_react7.default.createElement(
|
|
2970
|
+
SectionCard,
|
|
2858
2971
|
{
|
|
2859
|
-
|
|
2860
|
-
team
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
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
|
+
);
|
|
2867
2988
|
};
|
|
2868
2989
|
}
|
|
2869
2990
|
});
|
|
@@ -2886,20 +3007,20 @@ function fmtTokens(n) {
|
|
|
2886
3007
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
2887
3008
|
return String(n);
|
|
2888
3009
|
}
|
|
2889
|
-
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;
|
|
2890
3011
|
var init_ModelsTable = __esm({
|
|
2891
3012
|
"src/components/ModelsTable.tsx"() {
|
|
2892
3013
|
"use strict";
|
|
2893
3014
|
import_react8 = __toESM(require("react"));
|
|
2894
3015
|
import_Box8 = __toESM(require("@mui/material/Box"));
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
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"));
|
|
2901
3022
|
import_Typography8 = __toESM(require("@mui/material/Typography"));
|
|
2902
|
-
|
|
3023
|
+
import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
|
|
2903
3024
|
import_Select = __toESM(require("@mui/material/Select"));
|
|
2904
3025
|
import_FormControl = __toESM(require("@mui/material/FormControl"));
|
|
2905
3026
|
import_InputLabel = __toESM(require("@mui/material/InputLabel"));
|
|
@@ -2953,7 +3074,7 @@ var init_ModelsTable = __esm({
|
|
|
2953
3074
|
label: "Team",
|
|
2954
3075
|
onChange: (e) => setSelectedTeamId(e.target.value)
|
|
2955
3076
|
},
|
|
2956
|
-
teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(
|
|
3077
|
+
teams.map((t) => /* @__PURE__ */ import_react8.default.createElement(import_MenuItem4.default, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
|
|
2957
3078
|
))),
|
|
2958
3079
|
loading && /* @__PURE__ */ import_react8.default.createElement(EmptyState, { message: "Loading models\u2026" }),
|
|
2959
3080
|
!loading && !selectedTeamId && /* @__PURE__ */ import_react8.default.createElement(
|
|
@@ -2970,7 +3091,7 @@ var init_ModelsTable = __esm({
|
|
|
2970
3091
|
hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
|
|
2971
3092
|
}
|
|
2972
3093
|
),
|
|
2973
|
-
!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(
|
|
2974
3095
|
import_Box8.default,
|
|
2975
3096
|
{
|
|
2976
3097
|
component: "span",
|
|
@@ -2982,7 +3103,7 @@ var init_ModelsTable = __esm({
|
|
|
2982
3103
|
style: { cursor: "pointer", display: "inline-flex" }
|
|
2983
3104
|
},
|
|
2984
3105
|
/* @__PURE__ */ import_react8.default.createElement(import_ContentCopy.default, { sx: { fontSize: 14 } })
|
|
2985
|
-
)))), /* @__PURE__ */ import_react8.default.createElement(
|
|
3106
|
+
)))), /* @__PURE__ */ import_react8.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react8.default.createElement(
|
|
2986
3107
|
import_Box8.default,
|
|
2987
3108
|
{
|
|
2988
3109
|
component: "span",
|
|
@@ -2999,7 +3120,7 @@ var init_ModelsTable = __esm({
|
|
|
2999
3120
|
}
|
|
3000
3121
|
},
|
|
3001
3122
|
m.mode
|
|
3002
|
-
)), /* @__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" })))))))),
|
|
3003
3124
|
/* @__PURE__ */ import_react8.default.createElement(
|
|
3004
3125
|
import_Snackbar.default,
|
|
3005
3126
|
{
|
|
@@ -3036,10 +3157,10 @@ function formatDateTime(iso) {
|
|
|
3036
3157
|
}
|
|
3037
3158
|
function renderAuditLogBody(loading, entries) {
|
|
3038
3159
|
if (loading) {
|
|
3039
|
-
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 })));
|
|
3040
3161
|
}
|
|
3041
3162
|
if (entries.length === 0) {
|
|
3042
|
-
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(
|
|
3043
3164
|
EmptyState,
|
|
3044
3165
|
{
|
|
3045
3166
|
message: "No audit events found",
|
|
@@ -3049,29 +3170,29 @@ function renderAuditLogBody(loading, entries) {
|
|
|
3049
3170
|
}
|
|
3050
3171
|
return entries.map((entry) => /* @__PURE__ */ import_react9.default.createElement(DetailRow, { key: entry.id, entry }));
|
|
3051
3172
|
}
|
|
3052
|
-
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;
|
|
3053
3174
|
var init_AuditLog = __esm({
|
|
3054
3175
|
"src/components/AuditLog.tsx"() {
|
|
3055
3176
|
"use strict";
|
|
3056
3177
|
import_react9 = __toESM(require("react"));
|
|
3057
3178
|
import_Box9 = __toESM(require("@mui/material/Box"));
|
|
3058
3179
|
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
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"));
|
|
3065
3186
|
import_TablePagination = __toESM(require("@mui/material/TablePagination"));
|
|
3066
3187
|
import_TextField5 = __toESM(require("@mui/material/TextField"));
|
|
3067
|
-
|
|
3188
|
+
import_MenuItem5 = __toESM(require("@mui/material/MenuItem"));
|
|
3068
3189
|
import_Skeleton5 = __toESM(require("@mui/material/Skeleton"));
|
|
3069
3190
|
import_Collapse2 = __toESM(require("@mui/material/Collapse"));
|
|
3070
3191
|
import_IconButton5 = __toESM(require("@mui/material/IconButton"));
|
|
3071
3192
|
import_Alert4 = __toESM(require("@mui/material/Alert"));
|
|
3072
3193
|
import_styles6 = require("@mui/material/styles");
|
|
3073
3194
|
import_icons_material6 = require("@mui/icons-material");
|
|
3074
|
-
|
|
3195
|
+
import_react_use2 = require("react-use");
|
|
3075
3196
|
init_ui();
|
|
3076
3197
|
ACTION_TONES = {
|
|
3077
3198
|
created: "success",
|
|
@@ -3090,7 +3211,7 @@ var init_AuditLog = __esm({
|
|
|
3090
3211
|
DetailRow = ({ entry }) => {
|
|
3091
3212
|
const [open, setOpen] = (0, import_react9.useState)(false);
|
|
3092
3213
|
const hasDetail = entry.before_value || entry.updated_values;
|
|
3093
|
-
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(
|
|
3094
3215
|
import_IconButton5.default,
|
|
3095
3216
|
{
|
|
3096
3217
|
size: "small",
|
|
@@ -3104,7 +3225,7 @@ var init_AuditLog = __esm({
|
|
|
3104
3225
|
})
|
|
3105
3226
|
},
|
|
3106
3227
|
/* @__PURE__ */ import_react9.default.createElement(import_icons_material6.KeyboardArrowDown, { fontSize: "small" })
|
|
3107
|
-
)), /* @__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(
|
|
3108
3229
|
import_Typography9.default,
|
|
3109
3230
|
{
|
|
3110
3231
|
variant: "body2",
|
|
@@ -3114,7 +3235,7 @@ var init_AuditLog = __esm({
|
|
|
3114
3235
|
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
|
|
3115
3236
|
},
|
|
3116
3237
|
entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
|
|
3117
|
-
)), /* @__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(
|
|
3118
3239
|
import_Box9.default,
|
|
3119
3240
|
{
|
|
3120
3241
|
display: "flex",
|
|
@@ -3159,7 +3280,7 @@ var init_AuditLog = __esm({
|
|
|
3159
3280
|
() => ({ page: page + 1, page_size: pageSize, ...filters }),
|
|
3160
3281
|
[page, pageSize, filters]
|
|
3161
3282
|
)();
|
|
3162
|
-
const { value, loading, error } = (0,
|
|
3283
|
+
const { value, loading, error } = (0, import_react_use2.useAsync)(
|
|
3163
3284
|
() => api.getAuditLogs(fetchParams),
|
|
3164
3285
|
[api, fetchParams]
|
|
3165
3286
|
);
|
|
@@ -3184,11 +3305,11 @@ var init_AuditLog = __esm({
|
|
|
3184
3305
|
},
|
|
3185
3306
|
sx: { minWidth: 150 }
|
|
3186
3307
|
},
|
|
3187
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3188
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3189
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3190
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3191
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3308
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "" }, "All actions"),
|
|
3309
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "created" }, "Created"),
|
|
3310
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "updated" }, "Updated"),
|
|
3311
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "deleted" }, "Deleted"),
|
|
3312
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "blocked" }, "Blocked")
|
|
3192
3313
|
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3193
3314
|
import_TextField5.default,
|
|
3194
3315
|
{
|
|
@@ -3202,12 +3323,12 @@ var init_AuditLog = __esm({
|
|
|
3202
3323
|
},
|
|
3203
3324
|
sx: { minWidth: 150 }
|
|
3204
3325
|
},
|
|
3205
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3206
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3207
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3208
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3209
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3210
|
-
/* @__PURE__ */ import_react9.default.createElement(
|
|
3326
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "" }, "All tables"),
|
|
3327
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "LiteLLM_VerificationToken" }, "Key"),
|
|
3328
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamTable" }, "Team"),
|
|
3329
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "LiteLLM_TeamMembership" }, "Team member"),
|
|
3330
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
|
|
3331
|
+
/* @__PURE__ */ import_react9.default.createElement(import_MenuItem5.default, { value: "LiteLLM_UserTable" }, "User")
|
|
3211
3332
|
), /* @__PURE__ */ import_react9.default.createElement(
|
|
3212
3333
|
import_TextField5.default,
|
|
3213
3334
|
{
|
|
@@ -3223,7 +3344,7 @@ var init_AuditLog = __esm({
|
|
|
3223
3344
|
))
|
|
3224
3345
|
},
|
|
3225
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)),
|
|
3226
|
-
/* @__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)))),
|
|
3227
3348
|
/* @__PURE__ */ import_react9.default.createElement(
|
|
3228
3349
|
import_TablePagination.default,
|
|
3229
3350
|
{
|
|
@@ -3293,7 +3414,7 @@ function initDateRange() {
|
|
|
3293
3414
|
else start.setDate(start.getDate() - 7);
|
|
3294
3415
|
return { start, end };
|
|
3295
3416
|
}
|
|
3296
|
-
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;
|
|
3297
3418
|
var init_LiteLLMPage = __esm({
|
|
3298
3419
|
"src/components/LiteLLMPage.tsx"() {
|
|
3299
3420
|
"use strict";
|
|
@@ -3301,14 +3422,13 @@ var init_LiteLLMPage = __esm({
|
|
|
3301
3422
|
import_Box10 = __toESM(require("@mui/material/Box"));
|
|
3302
3423
|
import_Snackbar2 = __toESM(require("@mui/material/Snackbar"));
|
|
3303
3424
|
import_Alert5 = __toESM(require("@mui/material/Alert"));
|
|
3304
|
-
|
|
3425
|
+
import_CircularProgress5 = __toESM(require("@mui/material/CircularProgress"));
|
|
3305
3426
|
import_Typography10 = __toESM(require("@mui/material/Typography"));
|
|
3306
|
-
|
|
3427
|
+
import_Paper6 = __toESM(require("@mui/material/Paper"));
|
|
3307
3428
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
3308
3429
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
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");
|
|
3312
3432
|
import_plugin_permission_react = require("@backstage/plugin-permission-react");
|
|
3313
3433
|
init_DashboardHeader();
|
|
3314
3434
|
init_KeysTable();
|
|
@@ -3322,7 +3442,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3322
3442
|
init_permissions();
|
|
3323
3443
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
3324
3444
|
LiteLLMPage = () => {
|
|
3325
|
-
const api = (0,
|
|
3445
|
+
const api = (0, import_core_plugin_api3.useApi)(liteLlmApiRef);
|
|
3326
3446
|
const [dateRange, setDateRange] = (0, import_react10.useState)(initDateRange);
|
|
3327
3447
|
const [currentPreset, setCurrentPreset] = (0, import_react10.useState)(() => {
|
|
3328
3448
|
try {
|
|
@@ -3337,11 +3457,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3337
3457
|
const [manageTeam, setManageTeam] = (0, import_react10.useState)(null);
|
|
3338
3458
|
const [teamUsageCache, setTeamUsageCache] = (0, import_react10.useState)({});
|
|
3339
3459
|
const [teamUsageLoading, setTeamUsageLoading] = (0, import_react10.useState)({});
|
|
3340
|
-
const { value: userInfo, loading: userLoading, error: userError } = (0,
|
|
3460
|
+
const { value: userInfo, loading: userLoading, error: userError } = (0, import_react_use3.useAsync)(
|
|
3341
3461
|
() => api.getUserInfo(),
|
|
3342
3462
|
[api]
|
|
3343
3463
|
);
|
|
3344
|
-
const { value: keys, loading: keysLoading, retry: refreshKeys } = (0,
|
|
3464
|
+
const { value: keys, loading: keysLoading, retry: refreshKeys } = (0, import_react_use3.useAsyncRetry)(
|
|
3345
3465
|
async () => {
|
|
3346
3466
|
try {
|
|
3347
3467
|
return await api.listKeys();
|
|
@@ -3352,16 +3472,16 @@ var init_LiteLLMPage = __esm({
|
|
|
3352
3472
|
},
|
|
3353
3473
|
[api]
|
|
3354
3474
|
);
|
|
3355
|
-
const { value: allModels, loading: modelsLoading } = (0,
|
|
3475
|
+
const { value: allModels, loading: modelsLoading } = (0, import_react_use3.useAsync)(
|
|
3356
3476
|
() => api.listModels().catch(() => []),
|
|
3357
3477
|
[api]
|
|
3358
3478
|
);
|
|
3359
|
-
const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = (0,
|
|
3479
|
+
const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = (0, import_react_use3.useAsyncRetry)(
|
|
3360
3480
|
() => api.getTeams().catch(() => []),
|
|
3361
3481
|
[api]
|
|
3362
3482
|
);
|
|
3363
|
-
const { value: liteLlmConfig } = (0,
|
|
3364
|
-
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)(
|
|
3365
3485
|
async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
|
|
3366
3486
|
[api, liteLlmConfig]
|
|
3367
3487
|
);
|
|
@@ -3372,11 +3492,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3372
3492
|
const { allowed: canManageMcpServers } = (0, import_plugin_permission_react.usePermission)({ permission: litellmTeamMcpManagePermission });
|
|
3373
3493
|
const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
|
|
3374
3494
|
const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
|
|
3375
|
-
const { value: vectorStores } = (0,
|
|
3495
|
+
const { value: vectorStores } = (0, import_react_use3.useAsync)(
|
|
3376
3496
|
async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
|
|
3377
3497
|
[api, objectPermsEnabled]
|
|
3378
3498
|
);
|
|
3379
|
-
const { value: mcpServers } = (0,
|
|
3499
|
+
const { value: mcpServers } = (0, import_react_use3.useAsync)(
|
|
3380
3500
|
async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
|
|
3381
3501
|
[api, objectPermsEnabled]
|
|
3382
3502
|
);
|
|
@@ -3392,7 +3512,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3392
3512
|
);
|
|
3393
3513
|
return byMembership.length > 0 ? byMembership : allTeams;
|
|
3394
3514
|
}, [allTeams, userInfo]);
|
|
3395
|
-
const { value: usage, loading: usageLoading } = (0,
|
|
3515
|
+
const { value: usage, loading: usageLoading } = (0, import_react_use3.useAsync)(async () => {
|
|
3396
3516
|
const startDate = dateRange.start.toISOString().split("T")[0];
|
|
3397
3517
|
const endDate = dateRange.end.toISOString().split("T")[0];
|
|
3398
3518
|
return api.getUsage(startDate, endDate);
|
|
@@ -3527,12 +3647,12 @@ var init_LiteLLMPage = __esm({
|
|
|
3527
3647
|
);
|
|
3528
3648
|
const isInitialLoading = userLoading && !userInfo;
|
|
3529
3649
|
if (isInitialLoading) {
|
|
3530
|
-
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));
|
|
3531
3651
|
}
|
|
3532
3652
|
if (userError || !userInfo) {
|
|
3533
3653
|
const isProvisioningEnabled = userError?.body?.provisioning === true;
|
|
3534
3654
|
const hint = userError?.body?.hint;
|
|
3535
|
-
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.")));
|
|
3536
3656
|
}
|
|
3537
3657
|
const pageTabs = /* @__PURE__ */ import_react10.default.createElement(
|
|
3538
3658
|
import_Tabs2.default,
|
|
@@ -3593,14 +3713,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3593
3713
|
for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
|
|
3594
3714
|
for (const t of teams ?? []) teamsById.set(t.team_id, t);
|
|
3595
3715
|
const visibleTeams = Array.from(teamsById.values());
|
|
3596
|
-
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3597
|
-
import_Button6.default,
|
|
3598
|
-
{
|
|
3599
|
-
variant: "contained",
|
|
3600
|
-
onClick: () => setManageTeam({ mode: "create" })
|
|
3601
|
-
},
|
|
3602
|
-
"Create Team"
|
|
3603
|
-
)), /* @__PURE__ */ import_react10.default.createElement(
|
|
3716
|
+
return /* @__PURE__ */ import_react10.default.createElement(
|
|
3604
3717
|
TeamUsage,
|
|
3605
3718
|
{
|
|
3606
3719
|
teams: visibleTeams,
|
|
@@ -3611,9 +3724,11 @@ var init_LiteLLMPage = __esm({
|
|
|
3611
3724
|
},
|
|
3612
3725
|
getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
|
|
3613
3726
|
canManage: teamMgmtEnabled && canManageTeam,
|
|
3614
|
-
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
|
|
3727
|
+
onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
|
|
3728
|
+
canCreate: teamMgmtEnabled && canCreateTeam,
|
|
3729
|
+
onCreateTeam: () => setManageTeam({ mode: "create" })
|
|
3615
3730
|
}
|
|
3616
|
-
)
|
|
3731
|
+
);
|
|
3617
3732
|
})(), activeTab === "models" && /* @__PURE__ */ import_react10.default.createElement(
|
|
3618
3733
|
ModelsTable,
|
|
3619
3734
|
{
|
|
@@ -3769,17 +3884,17 @@ init_TeamUsage();
|
|
|
3769
3884
|
|
|
3770
3885
|
// src/components/LiteLLMHomeWidget.tsx
|
|
3771
3886
|
var import_react12 = __toESM(require("react"));
|
|
3772
|
-
var
|
|
3887
|
+
var import_Paper7 = __toESM(require("@mui/material/Paper"));
|
|
3773
3888
|
var import_Box11 = __toESM(require("@mui/material/Box"));
|
|
3774
3889
|
var import_Typography11 = __toESM(require("@mui/material/Typography"));
|
|
3775
3890
|
var import_FormControl2 = __toESM(require("@mui/material/FormControl"));
|
|
3776
3891
|
var import_Select2 = __toESM(require("@mui/material/Select"));
|
|
3777
|
-
var
|
|
3892
|
+
var import_MenuItem6 = __toESM(require("@mui/material/MenuItem"));
|
|
3778
3893
|
var import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
3779
|
-
var
|
|
3894
|
+
var import_CircularProgress6 = __toESM(require("@mui/material/CircularProgress"));
|
|
3780
3895
|
var import_Alert6 = __toESM(require("@mui/material/Alert"));
|
|
3781
3896
|
var import_recharts3 = require("recharts");
|
|
3782
|
-
var
|
|
3897
|
+
var import_core_plugin_api4 = require("@backstage/core-plugin-api");
|
|
3783
3898
|
init_api();
|
|
3784
3899
|
init_format();
|
|
3785
3900
|
function presetToDateRange(preset) {
|
|
@@ -3799,7 +3914,7 @@ var LiteLLMHomeWidget = ({
|
|
|
3799
3914
|
defaultPeriod = "7d",
|
|
3800
3915
|
title = "LiteLLM Usage"
|
|
3801
3916
|
}) => {
|
|
3802
|
-
const api = (0,
|
|
3917
|
+
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
3803
3918
|
const [period, setPeriod] = (0, import_react12.useState)(defaultPeriod);
|
|
3804
3919
|
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
3805
3920
|
const [usageError, setUsageError] = (0, import_react12.useState)(null);
|
|
@@ -3843,17 +3958,17 @@ var LiteLLMHomeWidget = ({
|
|
|
3843
3958
|
spend: d.spend
|
|
3844
3959
|
}));
|
|
3845
3960
|
const hasSparkline = dailyData.length > 0;
|
|
3846
|
-
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(
|
|
3847
3962
|
import_Select2.default,
|
|
3848
3963
|
{
|
|
3849
3964
|
value: period,
|
|
3850
3965
|
onChange: (e) => setPeriod(e.target.value),
|
|
3851
3966
|
displayEmpty: true
|
|
3852
3967
|
},
|
|
3853
|
-
/* @__PURE__ */ import_react12.default.createElement(
|
|
3854
|
-
/* @__PURE__ */ import_react12.default.createElement(
|
|
3855
|
-
/* @__PURE__ */ import_react12.default.createElement(
|
|
3856
|
-
))), loading && /* @__PURE__ */ import_react12.default.createElement(import_Box11.default, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ import_react12.default.createElement(
|
|
3968
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "today" }, "Today"),
|
|
3969
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "7d" }, "7d"),
|
|
3970
|
+
/* @__PURE__ */ import_react12.default.createElement(import_MenuItem6.default, { value: "30d" }, "30d")
|
|
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(
|
|
3857
3972
|
import_recharts3.Area,
|
|
3858
3973
|
{
|
|
3859
3974
|
type: "monotone",
|