@acarmisc/backstage-plugin-litellm 0.18.0 → 0.19.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
@@ -3004,6 +3004,9 @@ import MenuItem4 from "@mui/material/MenuItem";
3004
3004
  import Select from "@mui/material/Select";
3005
3005
  import FormControl from "@mui/material/FormControl";
3006
3006
  import InputLabel from "@mui/material/InputLabel";
3007
+ import TextField5 from "@mui/material/TextField";
3008
+ import InputAdornment2 from "@mui/material/InputAdornment";
3009
+ import { Search as Search2 } from "@mui/icons-material";
3007
3010
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
3008
3011
  import Tooltip3 from "@mui/material/Tooltip";
3009
3012
  import Snackbar from "@mui/material/Snackbar";
@@ -3035,15 +3038,23 @@ var init_ModelsTable = __esm({
3035
3038
  ModelsTable = ({ allModels, teams, loading }) => {
3036
3039
  const theme = useTheme2();
3037
3040
  const [selectedTeamId, setSelectedTeamId] = useState6("");
3041
+ const [filterText, setFilterText] = useState6("");
3038
3042
  const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
3039
3043
  const selectedTeam = useMemo6(
3040
3044
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
3041
3045
  [teams, selectedTeamId]
3042
3046
  );
3043
- const filteredModels = useMemo6(() => {
3047
+ const teamModels = useMemo6(() => {
3044
3048
  if (!selectedTeam) return [];
3045
3049
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
3046
3050
  }, [allModels, selectedTeam]);
3051
+ const filteredModels = useMemo6(() => {
3052
+ const q = filterText.trim().toLowerCase();
3053
+ if (!q) return teamModels;
3054
+ return teamModels.filter(
3055
+ (model) => model.model_name.toLowerCase().includes(q) || (model.access_groups || []).some((group) => group.toLowerCase().includes(q))
3056
+ );
3057
+ }, [teamModels, filterText]);
3047
3058
  const handleCopyModelId = useCallback((modelId) => {
3048
3059
  navigator.clipboard.writeText(modelId).then(() => setCopiedSnackbar(true));
3049
3060
  }, []);
@@ -3065,9 +3076,9 @@ var init_ModelsTable = __esm({
3065
3076
  SectionCard,
3066
3077
  {
3067
3078
  title: "Available Models",
3068
- subtitle: selectedTeam ? `${filteredModels.length} model${filteredModels.length !== 1 ? "s" : ""} in ${selectedTeam.team_alias ?? selectedTeam.team_id}` : "Select a team to view its models"
3079
+ subtitle: selectedTeam ? `${filteredModels.length}${filterText.trim() ? ` of ${teamModels.length}` : ""} model${filteredModels.length === 1 ? "" : "s"}` : "Select a team to view its models"
3069
3080
  },
3070
- /* @__PURE__ */ React8.createElement(Box8, { sx: { px: 2.5, pb: 2.5, pt: 1 } }, /* @__PURE__ */ React8.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React8.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React8.createElement(
3081
+ /* @__PURE__ */ React8.createElement(Box8, { sx: { px: 2.5, pb: 2.5, pt: 1, display: "flex", gap: 2, flexWrap: "wrap" } }, /* @__PURE__ */ React8.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React8.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React8.createElement(
3071
3082
  Select,
3072
3083
  {
3073
3084
  labelId: "team-select-label",
@@ -3076,7 +3087,20 @@ var init_ModelsTable = __esm({
3076
3087
  onChange: (e) => setSelectedTeamId(e.target.value)
3077
3088
  },
3078
3089
  teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3079
- ))),
3090
+ )), /* @__PURE__ */ React8.createElement(
3091
+ TextField5,
3092
+ {
3093
+ label: "Filter Models",
3094
+ size: "small",
3095
+ value: filterText,
3096
+ onChange: (e) => setFilterText(e.target.value),
3097
+ disabled: !selectedTeamId,
3098
+ sx: { minWidth: 240, mb: 2 },
3099
+ InputProps: {
3100
+ startAdornment: /* @__PURE__ */ React8.createElement(InputAdornment2, { position: "start" }, /* @__PURE__ */ React8.createElement(Search2, { fontSize: "small" }))
3101
+ }
3102
+ }
3103
+ )),
3080
3104
  loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
3081
3105
  !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
3082
3106
  EmptyState,
@@ -3085,14 +3109,32 @@ var init_ModelsTable = __esm({
3085
3109
  hint: "Each team may have a different set of models assigned"
3086
3110
  }
3087
3111
  ),
3088
- !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React8.createElement(
3112
+ !loading && selectedTeamId && teamModels.length === 0 && /* @__PURE__ */ React8.createElement(
3089
3113
  EmptyState,
3090
3114
  {
3091
3115
  message: "No models available for this team",
3092
3116
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
3093
3117
  }
3094
3118
  ),
3095
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer5, null, /* @__PURE__ */ React8.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead5, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
3119
+ !loading && selectedTeamId && teamModels.length > 0 && filteredModels.length === 0 && /* @__PURE__ */ React8.createElement(
3120
+ EmptyState,
3121
+ {
3122
+ message: "No models match your search",
3123
+ hint: "Try a different search term"
3124
+ }
3125
+ ),
3126
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer5, null, /* @__PURE__ */ React8.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead5, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody5, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow5, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(Tooltip3, { title: m.model_name, placement: "top" }, /* @__PURE__ */ React8.createElement(
3127
+ "span",
3128
+ {
3129
+ role: "button",
3130
+ tabIndex: 0,
3131
+ "aria-label": "Copy model ID",
3132
+ onClick: () => handleCopyModelId(m.model_name),
3133
+ onKeyDown: (e) => e.key === "Enter" && handleCopyModelId(m.model_name),
3134
+ style: { cursor: "pointer", display: "inline-flex" }
3135
+ },
3136
+ /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: "Copy model ID" })
3137
+ )), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
3096
3138
  Box8,
3097
3139
  {
3098
3140
  component: "span",
@@ -3148,7 +3190,7 @@ import TableContainer6 from "@mui/material/TableContainer";
3148
3190
  import TableHead6 from "@mui/material/TableHead";
3149
3191
  import TableRow6 from "@mui/material/TableRow";
3150
3192
  import TablePagination from "@mui/material/TablePagination";
3151
- import TextField5 from "@mui/material/TextField";
3193
+ import TextField6 from "@mui/material/TextField";
3152
3194
  import MenuItem5 from "@mui/material/MenuItem";
3153
3195
  import Skeleton5 from "@mui/material/Skeleton";
3154
3196
  import Collapse2 from "@mui/material/Collapse";
@@ -3294,7 +3336,7 @@ var init_AuditLog = __esm({
3294
3336
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
3295
3337
  flush: true,
3296
3338
  actions: /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
3297
- TextField5,
3339
+ TextField6,
3298
3340
  {
3299
3341
  size: "small",
3300
3342
  label: "Action",
@@ -3312,7 +3354,7 @@ var init_AuditLog = __esm({
3312
3354
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3313
3355
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3314
3356
  ), /* @__PURE__ */ React9.createElement(
3315
- TextField5,
3357
+ TextField6,
3316
3358
  {
3317
3359
  size: "small",
3318
3360
  label: "Table",
@@ -3331,7 +3373,7 @@ var init_AuditLog = __esm({
3331
3373
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3332
3374
  /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3333
3375
  ), /* @__PURE__ */ React9.createElement(
3334
- TextField5,
3376
+ TextField6,
3335
3377
  {
3336
3378
  size: "small",
3337
3379
  label: "Changed by",