@acarmisc/backstage-plugin-litellm 0.21.2 → 0.22.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/index.esm.js CHANGED
@@ -3393,6 +3393,7 @@ import TableCell5 from "@mui/material/TableCell";
3393
3393
  import TableContainer5 from "@mui/material/TableContainer";
3394
3394
  import TableHead5 from "@mui/material/TableHead";
3395
3395
  import TableRow5 from "@mui/material/TableRow";
3396
+ import TableSortLabel2 from "@mui/material/TableSortLabel";
3396
3397
  import Typography9 from "@mui/material/Typography";
3397
3398
  import MenuItem4 from "@mui/material/MenuItem";
3398
3399
  import Select from "@mui/material/Select";
@@ -3434,6 +3435,8 @@ var init_ModelsTable = __esm({
3434
3435
  const [selectedTeamId, setSelectedTeamId] = useState7("");
3435
3436
  const [filterText, setFilterText] = useState7("");
3436
3437
  const [copiedSnackbar, setCopiedSnackbar] = useState7(false);
3438
+ const [sortKey, setSortKey] = useState7("model_name");
3439
+ const [sortDirection, setSortDirection] = useState7("asc");
3437
3440
  const selectedTeam = useMemo7(
3438
3441
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3439
3442
  [teams, selectedTeamId]
@@ -3444,14 +3447,62 @@ var init_ModelsTable = __esm({
3444
3447
  }, [allModels, selectedTeam]);
3445
3448
  const filteredModels = useMemo7(() => {
3446
3449
  const q = filterText.trim().toLowerCase();
3447
- if (!q) return teamModels;
3448
- return teamModels.filter(
3449
- (model) => model.model_name.toLowerCase().includes(q) || (model.access_groups || []).some((group) => group.toLowerCase().includes(q))
3450
- );
3451
- }, [teamModels, filterText]);
3450
+ let models = teamModels;
3451
+ if (q) {
3452
+ models = teamModels.filter(
3453
+ (model) => model.model_name.toLowerCase().includes(q) || (model.access_groups || []).some((group) => group.toLowerCase().includes(q))
3454
+ );
3455
+ }
3456
+ return [...models].sort((a, b) => {
3457
+ let aVal;
3458
+ let bVal;
3459
+ switch (sortKey) {
3460
+ case "model_name":
3461
+ aVal = a.model_name;
3462
+ bVal = b.model_name;
3463
+ break;
3464
+ case "mode":
3465
+ aVal = a.mode;
3466
+ bVal = b.mode;
3467
+ break;
3468
+ case "input_cost_per_token":
3469
+ aVal = a.input_cost_per_token ?? 0;
3470
+ bVal = b.input_cost_per_token ?? 0;
3471
+ break;
3472
+ case "output_cost_per_token":
3473
+ aVal = a.output_cost_per_token ?? 0;
3474
+ bVal = b.output_cost_per_token ?? 0;
3475
+ break;
3476
+ case "max_input_tokens":
3477
+ aVal = a.max_input_tokens ?? 0;
3478
+ bVal = b.max_input_tokens ?? 0;
3479
+ break;
3480
+ case "max_output_tokens":
3481
+ aVal = a.max_output_tokens ?? 0;
3482
+ bVal = b.max_output_tokens ?? 0;
3483
+ break;
3484
+ default:
3485
+ return 0;
3486
+ }
3487
+ if (aVal === void 0 || aVal === null) aVal = "";
3488
+ if (bVal === void 0 || bVal === null) bVal = "";
3489
+ if (typeof aVal === "string" && typeof bVal === "string") {
3490
+ return sortDirection === "asc" ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
3491
+ }
3492
+ return sortDirection === "asc" ? aVal - bVal : bVal - aVal;
3493
+ });
3494
+ }, [teamModels, filterText, sortKey, sortDirection]);
3452
3495
  const handleCopyModelId = useCallback((modelId) => {
3453
3496
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3454
3497
  }, []);
3498
+ const handleSort = useCallback((key) => {
3499
+ if (sortKey === key) {
3500
+ setSortDirection((prev) => prev === "asc" ? "desc" : "asc");
3501
+ } else {
3502
+ setSortKey(key);
3503
+ setSortDirection("asc");
3504
+ }
3505
+ }, [sortKey]);
3455
3506
  const modeColor = (mode) => {
3456
3507
  switch (mode) {
3457
3508
  case "chat":
@@ -3517,7 +3568,7 @@ var init_ModelsTable = __esm({
3517
3568
  hint: "Try a different search term"
3518
3569
  }
3519
3570
  ),
3520
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React9.createElement(TableContainer5, null, /* @__PURE__ */ React9.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead5, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React9.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React9.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React9.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React9.createElement(
3571
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React9.createElement(TableContainer5, null, /* @__PURE__ */ React9.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead5, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sortDirection: sortKey === "model_name" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "model_name", direction: sortKey === "model_name" ? sortDirection : "asc", onClick: () => handleSort("model_name") }, "Model ID")), /* @__PURE__ */ React9.createElement(TableCell5, { sortDirection: sortKey === "mode" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "mode", direction: sortKey === "mode" ? sortDirection : "asc", onClick: () => handleSort("mode") }, "Mode")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "input_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "input_cost_per_token", direction: sortKey === "input_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("input_cost_per_token") }, "Input Cost")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "output_cost_per_token" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "output_cost_per_token", direction: sortKey === "output_cost_per_token" ? sortDirection : "asc", onClick: () => handleSort("output_cost_per_token") }, "Output Cost")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_input_tokens" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "max_input_tokens", direction: sortKey === "max_input_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_input_tokens") }, "Max Input")), /* @__PURE__ */ React9.createElement(TableCell5, { align: "right", sortDirection: sortKey === "max_output_tokens" ? sortDirection : false }, /* @__PURE__ */ React9.createElement(TableSortLabel2, { active: sortKey === "max_output_tokens", direction: sortKey === "max_output_tokens" ? sortDirection : "asc", onClick: () => handleSort("max_output_tokens") }, "Max Output")), /* @__PURE__ */ React9.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React9.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React9.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Box9, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React9.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React9.createElement(
3521
3572
  "span",
3522
3573
  {
3523
3574
  role: "button",
@@ -3574,7 +3625,7 @@ var init_ModelsTable = __esm({
3574
3625
  });
3575
3626
 
3576
3627
  // src/components/AuditLog.tsx
3577
- import React10, { useState as useState8, useCallback as useCallback2 } from "react";
3628
+ import React10, { useState as useState8, useMemo as useMemo8 } from "react";
3578
3629
  import Box10 from "@mui/material/Box";
3579
3630
  import Typography10 from "@mui/material/Typography";
3580
3631
  import Table6 from "@mui/material/Table";
@@ -3713,10 +3764,10 @@ var init_AuditLog = __esm({
3713
3764
  const [page, setPage] = useState8(0);
3714
3765
  const [pageSize, setPageSize] = useState8(25);
3715
3766
  const [filters, setFilters] = useState8({});
3716
- const fetchParams = useCallback2(
3767
+ const fetchParams = useMemo8(
3717
3768
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3718
3769
  [page, pageSize, filters]
3719
- )();
3770
+ );
3720
3771
  const { value, loading, error } = useAsync2(
3721
3772
  () => api.getAuditLogs(fetchParams),
3722
3773
  [api, fetchParams]
@@ -3837,7 +3888,7 @@ var LiteLLMPage_exports = {};
3837
3888
  __export(LiteLLMPage_exports, {
3838
3889
  LiteLLMPage: () => LiteLLMPage
3839
3890
  });
3840
- import React11, { useState as useState9, useCallback as useCallback3, useMemo as useMemo8 } from "react";
3891
+ import React11, { useState as useState9, useCallback as useCallback2, useMemo as useMemo9 } from "react";
3841
3892
  import Box11 from "@mui/material/Box";
3842
3893
  import Snackbar2 from "@mui/material/Snackbar";
3843
3894
  import Alert6 from "@mui/material/Alert";
@@ -3898,7 +3949,7 @@ var init_LiteLLMPage = __esm({
3898
3949
  const t = searchParams.get("tab");
3899
3950
  return isPageTab(t) ? t : "overview";
3900
3951
  });
3901
- const selectTab = useCallback3(
3952
+ const selectTab = useCallback2(
3902
3953
  (tab) => {
3903
3954
  setActiveTab(tab);
3904
3955
  setSearchParams(
@@ -3960,7 +4011,7 @@ var init_LiteLLMPage = __esm({
3960
4011
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3961
4012
  [api, objectPermsEnabled]
3962
4013
  );
3963
- const teams = useMemo8(() => {
4014
+ const teams = useMemo9(() => {
3964
4015
  if (!allTeams?.length) return [];
3965
4016
  if (!userInfo) return allTeams;
3966
4017
  const userId = userInfo.user_id;
@@ -3977,7 +4028,7 @@ var init_LiteLLMPage = __esm({
3977
4028
  const endDate = dateRange.end.toISOString().split("T")[0];
3978
4029
  return api.getUsage(startDate, endDate);
3979
4030
  }, [api, dateRange]);
3980
- const handleDateRangeChange = useCallback3((range, preset) => {
4031
+ const handleDateRangeChange = useCallback2((range, preset) => {
3981
4032
  setDateRange(range);
3982
4033
  if (preset) {
3983
4034
  setCurrentPreset(preset);
@@ -3985,7 +4036,7 @@ var init_LiteLLMPage = __esm({
3985
4036
  setTeamUsageCache({});
3986
4037
  setTeamUsageLoading({});
3987
4038
  }, [setDateRange, setCurrentPreset]);
3988
- const loadTeamUsage = useCallback3(async (teamId) => {
4039
+ const loadTeamUsage = useCallback2(async (teamId) => {
3989
4040
  if (teamUsageCache[teamId] !== void 0 || teamUsageLoading[teamId]) return;
3990
4041
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: true }));
3991
4042
  try {
@@ -3999,7 +4050,7 @@ var init_LiteLLMPage = __esm({
3999
4050
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
4000
4051
  }
4001
4052
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
4002
- const allowedModels = useMemo8(() => {
4053
+ const allowedModels = useMemo9(() => {
4003
4054
  if (!allModels?.length) return [];
4004
4055
  const userModels = userInfo?.models;
4005
4056
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -4012,7 +4063,7 @@ var init_LiteLLMPage = __esm({
4012
4063
  ]);
4013
4064
  return allModels.filter((m) => allowed.has(m.model_name));
4014
4065
  }, [allModels, userInfo, teams]);
4015
- const handleGenerateKey = useCallback3(
4066
+ const handleGenerateKey = useCallback2(
4016
4067
  async (request) => {
4017
4068
  try {
4018
4069
  const response = await api.generateKey(request);
@@ -4026,7 +4077,7 @@ var init_LiteLLMPage = __esm({
4026
4077
  },
4027
4078
  [api, refreshKeys]
4028
4079
  );
4029
- const handleUpdateKey = useCallback3(
4080
+ const handleUpdateKey = useCallback2(
4030
4081
  async (keyId, request) => {
4031
4082
  try {
4032
4083
  await api.updateKey(keyId, request);
@@ -4039,7 +4090,7 @@ var init_LiteLLMPage = __esm({
4039
4090
  },
4040
4091
  [api, refreshKeys]
4041
4092
  );
4042
- const handleBlockKey = useCallback3(
4093
+ const handleBlockKey = useCallback2(
4043
4094
  async (keyId) => {
4044
4095
  try {
4045
4096
  await api.blockKey(keyId);
@@ -4051,7 +4102,7 @@ var init_LiteLLMPage = __esm({
4051
4102
  },
4052
4103
  [api, refreshKeys]
4053
4104
  );
4054
- const handleUnblockKey = useCallback3(
4105
+ const handleUnblockKey = useCallback2(
4055
4106
  async (keyId) => {
4056
4107
  try {
4057
4108
  await api.unblockKey(keyId);
@@ -4063,7 +4114,7 @@ var init_LiteLLMPage = __esm({
4063
4114
  },
4064
4115
  [api, refreshKeys]
4065
4116
  );
4066
- const handleResetKeySpend = useCallback3(
4117
+ const handleResetKeySpend = useCallback2(
4067
4118
  async (keyId) => {
4068
4119
  try {
4069
4120
  await api.resetKeySpend(keyId);
@@ -4075,7 +4126,7 @@ var init_LiteLLMPage = __esm({
4075
4126
  },
4076
4127
  [api, refreshKeys]
4077
4128
  );
4078
- const handleDeleteKey = useCallback3(
4129
+ const handleDeleteKey = useCallback2(
4079
4130
  async (keyId) => {
4080
4131
  try {
4081
4132
  await api.deleteKey(keyId);
@@ -4092,7 +4143,7 @@ var init_LiteLLMPage = __esm({
4092
4143
  },
4093
4144
  [api, refreshKeys]
4094
4145
  );
4095
- const handlePruneExpiredKeys = useCallback3(
4146
+ const handlePruneExpiredKeys = useCallback2(
4096
4147
  async () => {
4097
4148
  try {
4098
4149
  const result = await api.pruneExpiredKeys();