@acarmisc/backstage-plugin-litellm 0.21.1 → 0.21.3
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/index.cjs.js +102 -27
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.esm.js +101 -26
- package/dist/index.esm.js.map +3 -3
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -2855,7 +2855,7 @@ function levelToneColor(tone, theme) {
|
|
|
2855
2855
|
return theme.palette.primary.main;
|
|
2856
2856
|
}
|
|
2857
2857
|
}
|
|
2858
|
-
var import_react7, import_Paper5, import_Box7, import_Typography7, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles4, import_core_plugin_api3, LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
2858
|
+
var import_react7, import_Paper5, import_Box7, import_Typography7, import_CircularProgress4, import_Alert3, import_Collapse, import_IconButton4, import_icons_material5, import_styles4, import_core_plugin_api3, import_core_components, LevelFrame, EmptyLimitCard, LimitCard, CLOSEST_LEVEL_LABEL, KEYS_PATH, MoreKeysNote, HierarchyNote, TaggedLimit, FullBody, CompactBody, LiteLLMBudgetWidget;
|
|
2859
2859
|
var init_LiteLLMBudgetWidget = __esm({
|
|
2860
2860
|
"src/components/LiteLLMBudgetWidget.tsx"() {
|
|
2861
2861
|
"use strict";
|
|
@@ -2870,6 +2870,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2870
2870
|
import_icons_material5 = require("@mui/icons-material");
|
|
2871
2871
|
import_styles4 = require("@mui/material/styles");
|
|
2872
2872
|
import_core_plugin_api3 = require("@backstage/core-plugin-api");
|
|
2873
|
+
import_core_components = require("@backstage/core-components");
|
|
2873
2874
|
init_api();
|
|
2874
2875
|
init_format();
|
|
2875
2876
|
init_ui();
|
|
@@ -2983,6 +2984,21 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
2983
2984
|
user: "personal",
|
|
2984
2985
|
team: "team"
|
|
2985
2986
|
};
|
|
2987
|
+
KEYS_PATH = "/litellm?tab=keys";
|
|
2988
|
+
MoreKeysNote = ({ count }) => /* @__PURE__ */ import_react7.default.createElement(import_core_components.Link, { to: KEYS_PATH, variant: "caption" }, "+", count, " more budgeted key", count > 1 ? "s" : "", " further from the cap \u2192");
|
|
2989
|
+
HierarchyNote = ({ withReset }) => /* @__PURE__ */ import_react7.default.createElement(
|
|
2990
|
+
import_Box7.default,
|
|
2991
|
+
{
|
|
2992
|
+
sx: (theme) => ({
|
|
2993
|
+
px: 1.5,
|
|
2994
|
+
py: 1.25,
|
|
2995
|
+
borderRadius: 1.5,
|
|
2996
|
+
border: "1px dashed",
|
|
2997
|
+
borderColor: (0, import_styles4.alpha)(theme.palette.text.primary, 0.18)
|
|
2998
|
+
})
|
|
2999
|
+
},
|
|
3000
|
+
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", display: "block" }, /* @__PURE__ */ import_react7.default.createElement("b", null, "Order:"), " key \u2192 personal \u2192 team \u2192 global. The first cap you reach blocks the request; a key that belongs to a team uses the team's cap, not your personal one.", withReset && " A cap with a reset window drops to $0 when the window closes; without one it never resets.")
|
|
3001
|
+
);
|
|
2986
3002
|
TaggedLimit = ({ limit }) => {
|
|
2987
3003
|
const tone = budgetTone2(limit.pct);
|
|
2988
3004
|
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, null, /* @__PURE__ */ import_react7.default.createElement(
|
|
@@ -3012,7 +3028,7 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3012
3028
|
},
|
|
3013
3029
|
!hasBudgetedKeys && /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No key has a budget, so no key-level cap applies."),
|
|
3014
3030
|
summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: k.sublabel ?? k.label, limit: k })),
|
|
3015
|
-
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(
|
|
3031
|
+
summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys })
|
|
3016
3032
|
), /* @__PURE__ */ import_react7.default.createElement(
|
|
3017
3033
|
LevelFrame,
|
|
3018
3034
|
{
|
|
@@ -3022,27 +3038,14 @@ var init_LiteLLMBudgetWidget = __esm({
|
|
|
3022
3038
|
note: "skipped for team-bound keys"
|
|
3023
3039
|
},
|
|
3024
3040
|
summary.user ? /* @__PURE__ */ import_react7.default.createElement(LimitCard, { limit: summary.user }) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No personal budget is set on your account.")
|
|
3025
|
-
), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ import_react7.default.createElement(
|
|
3026
|
-
import_Box7.default,
|
|
3027
|
-
{
|
|
3028
|
-
sx: (theme) => ({
|
|
3029
|
-
mt: 2,
|
|
3030
|
-
px: 1.5,
|
|
3031
|
-
py: 1.25,
|
|
3032
|
-
borderRadius: 1.5,
|
|
3033
|
-
border: "1px dashed",
|
|
3034
|
-
borderColor: (0, import_styles4.alpha)(theme.palette.text.primary, 0.18)
|
|
3035
|
-
})
|
|
3036
|
-
},
|
|
3037
|
-
/* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "caption", color: "text.secondary", display: "block" }, "The first cap a request runs into is the one that stops it. A limit with a reset window (e.g. daily or every 30 days) clears its spend to $0 when the window closes; without a window it never resets.")
|
|
3038
|
-
));
|
|
3041
|
+
), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 3, name: "Team", tagline: "shared cap for all keys in a team (if any)" }, summary.teams.length > 0 ? summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(LimitCard, { key: t.sublabel ?? t.label, limit: t })) : /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "No team you belong to has a budget.")), /* @__PURE__ */ import_react7.default.createElement(LevelFrame, { rank: 4, name: "Global", tagline: "proxy-wide cap set by your admin", isLast: true }, /* @__PURE__ */ import_react7.default.createElement(EmptyLimitCard, null, "Configured by your admin in the proxy config \u2014 it isn't visible from this page. When set, it limits spend across every request on the proxy.")), /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { mt: 2 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, { withReset: true })));
|
|
3039
3042
|
};
|
|
3040
3043
|
CompactBody = ({ summary }) => {
|
|
3041
3044
|
const { count } = budgetHeadline(summary);
|
|
3042
3045
|
if (count === 0) {
|
|
3043
3046
|
return /* @__PURE__ */ import_react7.default.createElement(import_Typography7.default, { variant: "body2", color: "text.secondary", sx: { fontSize: 13 } }, "No budget limits apply to you right now.");
|
|
3044
3047
|
}
|
|
3045
|
-
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(
|
|
3048
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_Box7.default, { sx: { display: "flex", flexDirection: "column", gap: 1.25 } }, /* @__PURE__ */ import_react7.default.createElement(HierarchyNote, null), summary.keys.map((k) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: k.sublabel ?? k.label, limit: k })), summary.hiddenBudgetedKeys > 0 && /* @__PURE__ */ import_react7.default.createElement(MoreKeysNote, { count: summary.hiddenBudgetedKeys }), summary.user && /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { limit: summary.user }), summary.teams.map((t) => /* @__PURE__ */ import_react7.default.createElement(TaggedLimit, { key: t.sublabel ?? t.label, limit: t })));
|
|
3046
3049
|
};
|
|
3047
3050
|
LiteLLMBudgetWidget = ({
|
|
3048
3051
|
title = "Budget Policy",
|
|
@@ -3398,7 +3401,7 @@ function fmtTokens(n) {
|
|
|
3398
3401
|
if (n >= 1e3) return `${(n / 1e3).toFixed(0)}K`;
|
|
3399
3402
|
return String(n);
|
|
3400
3403
|
}
|
|
3401
|
-
var import_react9, import_Box9, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_Typography9, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles6, ALL_PROXY_MODELS2, ModelsTable;
|
|
3404
|
+
var import_react9, import_Box9, import_Table5, import_TableBody5, import_TableCell5, import_TableContainer5, import_TableHead5, import_TableRow5, import_TableSortLabel2, import_Typography9, import_MenuItem4, import_Select, import_FormControl, import_InputLabel, import_TextField5, import_InputAdornment2, import_icons_material7, import_ContentCopy, import_Tooltip, import_Snackbar, import_Alert4, import_styles6, ALL_PROXY_MODELS2, ModelsTable;
|
|
3402
3405
|
var init_ModelsTable = __esm({
|
|
3403
3406
|
"src/components/ModelsTable.tsx"() {
|
|
3404
3407
|
"use strict";
|
|
@@ -3410,6 +3413,7 @@ var init_ModelsTable = __esm({
|
|
|
3410
3413
|
import_TableContainer5 = __toESM(require("@mui/material/TableContainer"));
|
|
3411
3414
|
import_TableHead5 = __toESM(require("@mui/material/TableHead"));
|
|
3412
3415
|
import_TableRow5 = __toESM(require("@mui/material/TableRow"));
|
|
3416
|
+
import_TableSortLabel2 = __toESM(require("@mui/material/TableSortLabel"));
|
|
3413
3417
|
import_Typography9 = __toESM(require("@mui/material/Typography"));
|
|
3414
3418
|
import_MenuItem4 = __toESM(require("@mui/material/MenuItem"));
|
|
3415
3419
|
import_Select = __toESM(require("@mui/material/Select"));
|
|
@@ -3430,6 +3434,8 @@ var init_ModelsTable = __esm({
|
|
|
3430
3434
|
const [selectedTeamId, setSelectedTeamId] = (0, import_react9.useState)("");
|
|
3431
3435
|
const [filterText, setFilterText] = (0, import_react9.useState)("");
|
|
3432
3436
|
const [copiedSnackbar, setCopiedSnackbar] = (0, import_react9.useState)(false);
|
|
3437
|
+
const [sortKey, setSortKey] = (0, import_react9.useState)("model_name");
|
|
3438
|
+
const [sortDirection, setSortDirection] = (0, import_react9.useState)("asc");
|
|
3433
3439
|
const selectedTeam = (0, import_react9.useMemo)(
|
|
3434
3440
|
() => teams.find((t) => t.team_id === selectedTeamId) ?? null,
|
|
3435
3441
|
[teams, selectedTeamId]
|
|
@@ -3440,14 +3446,62 @@ var init_ModelsTable = __esm({
|
|
|
3440
3446
|
}, [allModels, selectedTeam]);
|
|
3441
3447
|
const filteredModels = (0, import_react9.useMemo)(() => {
|
|
3442
3448
|
const q = filterText.trim().toLowerCase();
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3449
|
+
let models = teamModels;
|
|
3450
|
+
if (q) {
|
|
3451
|
+
models = teamModels.filter(
|
|
3452
|
+
(model) => model.model_name.toLowerCase().includes(q) || (model.access_groups || []).some((group) => group.toLowerCase().includes(q))
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3455
|
+
return [...models].sort((a, b) => {
|
|
3456
|
+
let aVal;
|
|
3457
|
+
let bVal;
|
|
3458
|
+
switch (sortKey) {
|
|
3459
|
+
case "model_name":
|
|
3460
|
+
aVal = a.model_name;
|
|
3461
|
+
bVal = b.model_name;
|
|
3462
|
+
break;
|
|
3463
|
+
case "mode":
|
|
3464
|
+
aVal = a.mode;
|
|
3465
|
+
bVal = b.mode;
|
|
3466
|
+
break;
|
|
3467
|
+
case "input_cost_per_token":
|
|
3468
|
+
aVal = a.input_cost_per_token ?? 0;
|
|
3469
|
+
bVal = b.input_cost_per_token ?? 0;
|
|
3470
|
+
break;
|
|
3471
|
+
case "output_cost_per_token":
|
|
3472
|
+
aVal = a.output_cost_per_token ?? 0;
|
|
3473
|
+
bVal = b.output_cost_per_token ?? 0;
|
|
3474
|
+
break;
|
|
3475
|
+
case "max_input_tokens":
|
|
3476
|
+
aVal = a.max_input_tokens ?? 0;
|
|
3477
|
+
bVal = b.max_input_tokens ?? 0;
|
|
3478
|
+
break;
|
|
3479
|
+
case "max_output_tokens":
|
|
3480
|
+
aVal = a.max_output_tokens ?? 0;
|
|
3481
|
+
bVal = b.max_output_tokens ?? 0;
|
|
3482
|
+
break;
|
|
3483
|
+
default:
|
|
3484
|
+
return 0;
|
|
3485
|
+
}
|
|
3486
|
+
if (aVal === void 0 || aVal === null) aVal = "";
|
|
3487
|
+
if (bVal === void 0 || bVal === null) bVal = "";
|
|
3488
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
3489
|
+
return sortDirection === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
|
|
3490
|
+
}
|
|
3491
|
+
return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
|
|
3492
|
+
});
|
|
3493
|
+
}, [teamModels, filterText, sortKey, sortDirection]);
|
|
3448
3494
|
const handleCopyModelId = (0, import_react9.useCallback)((modelId) => {
|
|
3449
3495
|
navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
|
|
3450
3496
|
}, []);
|
|
3497
|
+
const handleSort = (0, import_react9.useCallback)((key) => {
|
|
3498
|
+
if (sortKey === key) {
|
|
3499
|
+
setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
|
|
3500
|
+
} else {
|
|
3501
|
+
setSortKey(key);
|
|
3502
|
+
setSortDirection("asc");
|
|
3503
|
+
}
|
|
3504
|
+
}, [sortKey]);
|
|
3451
3505
|
const modeColor = (mode) => {
|
|
3452
3506
|
switch (mode) {
|
|
3453
3507
|
case "chat":
|
|
@@ -3513,7 +3567,7 @@ var init_ModelsTable = __esm({
|
|
|
3513
3567
|
hint: "Try a different search term"
|
|
3514
3568
|
}
|
|
3515
3569
|
),
|
|
3516
|
-
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react9.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default,
|
|
3570
|
+
!loading && filteredModels.length > 0 && /* @__PURE__ */ import_react9.default.createElement(import_TableContainer5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Table5.default, { size: "small", sx: dataTableSx }, /* @__PURE__ */ import_react9.default.createElement(import_TableHead5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ import_react9.default.createElement(import_TableSortLabel2.default, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, "Capabilities"))), /* @__PURE__ */ import_react9.default.createElement(import_TableBody5.default, null, filteredModels.map((m) => /* @__PURE__ */ import_react9.default.createElement(import_TableRow5.default, { key: m.model_name, hover: true }, /* @__PURE__ */ import_react9.default.createElement(import_TableCell5.default, null, /* @__PURE__ */ import_react9.default.createElement(import_Box9.default, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ import_react9.default.createElement(import_Tooltip.default, { title: m.model_name, placement: "top" }, /* @__PURE__ */ import_react9.default.createElement(
|
|
3517
3571
|
"span",
|
|
3518
3572
|
{
|
|
3519
3573
|
role: "button",
|
|
@@ -3847,7 +3901,7 @@ function initDateRange() {
|
|
|
3847
3901
|
else start.setDate(start.getDate() - 7);
|
|
3848
3902
|
return { start, end };
|
|
3849
3903
|
}
|
|
3850
|
-
var import_react11, import_Box11, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography11, import_Paper7, import_Tabs2, import_Tab2, import_Grid2, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, LiteLLMPage;
|
|
3904
|
+
var import_react11, import_Box11, import_Snackbar2, import_Alert6, import_CircularProgress6, import_Typography11, import_Paper7, import_Tabs2, import_Tab2, import_Grid2, import_react_router_dom, import_react_use3, import_core_plugin_api4, import_plugin_permission_react, PERIOD_LS_KEY2, PAGE_TABS, isPageTab, LiteLLMPage;
|
|
3851
3905
|
var init_LiteLLMPage = __esm({
|
|
3852
3906
|
"src/components/LiteLLMPage.tsx"() {
|
|
3853
3907
|
"use strict";
|
|
@@ -3861,6 +3915,7 @@ var init_LiteLLMPage = __esm({
|
|
|
3861
3915
|
import_Tabs2 = __toESM(require("@mui/material/Tabs"));
|
|
3862
3916
|
import_Tab2 = __toESM(require("@mui/material/Tab"));
|
|
3863
3917
|
import_Grid2 = __toESM(require("@mui/material/Grid"));
|
|
3918
|
+
import_react_router_dom = require("react-router-dom");
|
|
3864
3919
|
import_react_use3 = require("react-use");
|
|
3865
3920
|
import_core_plugin_api4 = require("@backstage/core-plugin-api");
|
|
3866
3921
|
import_plugin_permission_react = require("@backstage/plugin-permission-react");
|
|
@@ -3876,6 +3931,8 @@ var init_LiteLLMPage = __esm({
|
|
|
3876
3931
|
init_api();
|
|
3877
3932
|
init_permissions();
|
|
3878
3933
|
PERIOD_LS_KEY2 = "litellm_usage_period";
|
|
3934
|
+
PAGE_TABS = ["overview", "keys", "teams", "models", "audit"];
|
|
3935
|
+
isPageTab = (v) => !!v && PAGE_TABS.includes(v);
|
|
3879
3936
|
LiteLLMPage = () => {
|
|
3880
3937
|
const api = (0, import_core_plugin_api4.useApi)(liteLlmApiRef);
|
|
3881
3938
|
const [dateRange, setDateRange] = (0, import_react11.useState)(initDateRange);
|
|
@@ -3886,7 +3943,25 @@ var init_LiteLLMPage = __esm({
|
|
|
3886
3943
|
return "7d";
|
|
3887
3944
|
}
|
|
3888
3945
|
});
|
|
3889
|
-
const [
|
|
3946
|
+
const [searchParams, setSearchParams] = (0, import_react_router_dom.useSearchParams)();
|
|
3947
|
+
const [activeTab, setActiveTab] = (0, import_react11.useState)(() => {
|
|
3948
|
+
const t = searchParams.get("tab");
|
|
3949
|
+
return isPageTab(t) ? t : "overview";
|
|
3950
|
+
});
|
|
3951
|
+
const selectTab = (0, import_react11.useCallback)(
|
|
3952
|
+
(tab) => {
|
|
3953
|
+
setActiveTab(tab);
|
|
3954
|
+
setSearchParams(
|
|
3955
|
+
(prev) => {
|
|
3956
|
+
const next = new URLSearchParams(prev);
|
|
3957
|
+
next.set("tab", tab);
|
|
3958
|
+
return next;
|
|
3959
|
+
},
|
|
3960
|
+
{ replace: true }
|
|
3961
|
+
);
|
|
3962
|
+
},
|
|
3963
|
+
[setSearchParams]
|
|
3964
|
+
);
|
|
3890
3965
|
const [snackbar, setSnackbar] = (0, import_react11.useState)(null);
|
|
3891
3966
|
const [generateDialogOpen, setGenerateDialogOpen] = (0, import_react11.useState)(false);
|
|
3892
3967
|
const [manageTeam, setManageTeam] = (0, import_react11.useState)(null);
|
|
@@ -4093,7 +4168,7 @@ var init_LiteLLMPage = __esm({
|
|
|
4093
4168
|
import_Tabs2.default,
|
|
4094
4169
|
{
|
|
4095
4170
|
value: activeTab,
|
|
4096
|
-
onChange: (_, v) =>
|
|
4171
|
+
onChange: (_, v) => selectTab(v),
|
|
4097
4172
|
variant: "scrollable",
|
|
4098
4173
|
scrollButtons: "auto",
|
|
4099
4174
|
sx: {
|