@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/index.esm.js CHANGED
@@ -1920,7 +1920,7 @@ var init_GenerateKeyDialog = __esm({
1920
1920
  });
1921
1921
 
1922
1922
  // src/components/ManageTeamDialog.tsx
1923
- import React5, { useState as useState3, useEffect as useEffect2 } from "react";
1923
+ import React5, { useState as useState3, useEffect as useEffect2, useMemo as useMemo4 } from "react";
1924
1924
  import Dialog3 from "@mui/material/Dialog";
1925
1925
  import DialogTitle3 from "@mui/material/DialogTitle";
1926
1926
  import DialogContent3 from "@mui/material/DialogContent";
@@ -1931,18 +1931,38 @@ import Alert2 from "@mui/material/Alert";
1931
1931
  import Checkbox2 from "@mui/material/Checkbox";
1932
1932
  import FormControlLabel2 from "@mui/material/FormControlLabel";
1933
1933
  import Autocomplete3 from "@mui/material/Autocomplete";
1934
+ import CircularProgress3 from "@mui/material/CircularProgress";
1935
+ import MenuItem2 from "@mui/material/MenuItem";
1934
1936
  import Box5 from "@mui/material/Box";
1935
1937
  import Divider2 from "@mui/material/Divider";
1936
1938
  import Typography5 from "@mui/material/Typography";
1937
1939
  import IconButton3 from "@mui/material/IconButton";
1938
- import List from "@mui/material/List";
1939
- import ListItem from "@mui/material/ListItem";
1940
- import ListItemText from "@mui/material/ListItemText";
1940
+ import Paper3 from "@mui/material/Paper";
1941
+ import Table2 from "@mui/material/Table";
1942
+ import TableBody2 from "@mui/material/TableBody";
1943
+ import TableCell2 from "@mui/material/TableCell";
1944
+ import TableContainer2 from "@mui/material/TableContainer";
1945
+ import TableHead2 from "@mui/material/TableHead";
1946
+ import TableRow2 from "@mui/material/TableRow";
1941
1947
  import { Delete as DeleteIcon } from "@mui/icons-material";
1942
- var ManageTeamDialog;
1948
+ import { useApi } from "@backstage/core-plugin-api";
1949
+ import { catalogApiRef } from "@backstage/plugin-catalog-react";
1950
+ import { stringifyEntityRef } from "@backstage/catalog-model";
1951
+ import { useAsync } from "react-use";
1952
+ var BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
1943
1953
  var init_ManageTeamDialog = __esm({
1944
1954
  "src/components/ManageTeamDialog.tsx"() {
1945
1955
  "use strict";
1956
+ init_ui();
1957
+ BUDGET_DURATION_OPTIONS = [
1958
+ { value: "1d", label: "Daily (1d)" },
1959
+ { value: "7d", label: "Weekly (7d)" },
1960
+ { value: "30d", label: "Monthly (30d)" },
1961
+ { value: "90d", label: "Quarterly (90d)" },
1962
+ { value: "365d", label: "Yearly (365d)" }
1963
+ ];
1964
+ DEFAULT_BUDGET_DURATION = "30d";
1965
+ FALLBACK_BUDGET_CEILING = 1e3;
1946
1966
  ManageTeamDialog = ({
1947
1967
  open,
1948
1968
  onClose,
@@ -1968,6 +1988,7 @@ var init_ManageTeamDialog = __esm({
1968
1988
  const [budgetDuration, setBudgetDuration] = useState3("");
1969
1989
  const [submitting, setSubmitting] = useState3(false);
1970
1990
  const [error, setError] = useState3(null);
1991
+ const [memberQuery, setMemberQuery] = useState3("");
1971
1992
  const [memberRef, setMemberRef] = useState3("");
1972
1993
  const [memberBudget, setMemberBudget] = useState3("");
1973
1994
  const [memberBusy, setMemberBusy] = useState3(false);
@@ -1979,23 +2000,56 @@ var init_ManageTeamDialog = __esm({
1979
2000
  const [mcpBusy, setMcpBusy] = useState3(false);
1980
2001
  const [mcpError, setMcpError] = useState3(null);
1981
2002
  const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
1982
- const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling;
2003
+ const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
2004
+ const members = useMemo4(() => team?.members_with_roles ?? [], [team]);
2005
+ const showMembers = mode === "edit" && !!canManageMembers;
2006
+ const catalogApi = useApi(catalogApiRef);
2007
+ const { value: catalogUsers, loading: usersLoading } = useAsync(async () => {
2008
+ if (!open || !showMembers) return [];
2009
+ try {
2010
+ const { items } = await catalogApi.getEntities({
2011
+ filter: { kind: "User" },
2012
+ fields: ["kind", "metadata.namespace", "metadata.name", "metadata.title", "spec.profile"]
2013
+ });
2014
+ return items;
2015
+ } catch {
2016
+ return [];
2017
+ }
2018
+ }, [open, showMembers, catalogApi]);
2019
+ const userOptions = useMemo4(() => {
2020
+ const existingEmails = new Set(
2021
+ members.map((m) => m.user_email?.toLowerCase()).filter((e) => !!e)
2022
+ );
2023
+ return (catalogUsers ?? []).map((e) => {
2024
+ const ref = stringifyEntityRef(e);
2025
+ const displayName = e.spec?.profile?.displayName || e.metadata.title || e.metadata.name;
2026
+ const email = e.spec?.profile?.email;
2027
+ return {
2028
+ ref,
2029
+ email,
2030
+ label: email ? `${displayName} (${email})` : displayName
2031
+ };
2032
+ }).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
2033
+ }, [catalogUsers, members]);
1983
2034
  useEffect2(() => {
1984
2035
  if (open) {
1985
2036
  if (mode === "edit" && team) {
1986
2037
  setAlias(team.team_alias ?? "");
1987
2038
  setModels(team.models ?? []);
1988
- setMaxBudget(team.max_budget ? String(team.max_budget) : "");
2039
+ setMaxBudget(
2040
+ team.max_budget ? String(team.max_budget) : String(maxBudgetCeiling)
2041
+ );
1989
2042
  setUnlimited(allowUnlimitedBudget && !team.max_budget);
1990
- setBudgetDuration("");
2043
+ setBudgetDuration(team.budget_duration ?? DEFAULT_BUDGET_DURATION);
1991
2044
  } else {
1992
2045
  setAlias("");
1993
2046
  setModels([]);
1994
- setMaxBudget("");
2047
+ setMaxBudget(String(maxBudgetCeiling));
1995
2048
  setUnlimited(allowUnlimitedBudget);
1996
- setBudgetDuration("");
2049
+ setBudgetDuration(DEFAULT_BUDGET_DURATION);
1997
2050
  }
1998
2051
  setError(null);
2052
+ setMemberQuery("");
1999
2053
  setMemberRef("");
2000
2054
  setMemberBudget("");
2001
2055
  setMemberError(null);
@@ -2004,7 +2058,7 @@ var init_ManageTeamDialog = __esm({
2004
2058
  setMcpIds(team?.object_permission?.mcp_servers ?? []);
2005
2059
  setMcpError(null);
2006
2060
  }
2007
- }, [open, mode, team, allowUnlimitedBudget]);
2061
+ }, [open, mode, team, allowUnlimitedBudget, maxBudgetCeiling]);
2008
2062
  const handleSubmit = async () => {
2009
2063
  setError(null);
2010
2064
  if (!alias.trim()) {
@@ -2025,7 +2079,7 @@ var init_ManageTeamDialog = __esm({
2025
2079
  setError("Budget must be a positive number");
2026
2080
  return;
2027
2081
  }
2028
- if (maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 && budget > maxBudgetCeiling) {
2082
+ if (budget > maxBudgetCeiling) {
2029
2083
  setError(`Budget cannot exceed $${maxBudgetCeiling}`);
2030
2084
  return;
2031
2085
  }
@@ -2067,6 +2121,7 @@ var init_ManageTeamDialog = __esm({
2067
2121
  try {
2068
2122
  setMemberBusy(true);
2069
2123
  await onAddMember(memberRef.trim(), budget);
2124
+ setMemberQuery("");
2070
2125
  setMemberRef("");
2071
2126
  setMemberBudget("");
2072
2127
  } catch (err) {
@@ -2112,8 +2167,7 @@ var init_ManageTeamDialog = __esm({
2112
2167
  }
2113
2168
  };
2114
2169
  const modelNames = allModels.map((m) => m.model_name);
2115
- const members = team?.members_with_roles ?? [];
2116
- const showMembers = mode === "edit" && !!canManageMembers;
2170
+ const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
2117
2171
  const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
2118
2172
  const kbOptions = (vectorStores ?? []).map((v) => v.id);
2119
2173
  const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
@@ -2137,7 +2191,15 @@ var init_ManageTeamDialog = __esm({
2137
2191
  value: models,
2138
2192
  onChange: (_, newValue) => setModels(newValue),
2139
2193
  disabled: submitting,
2140
- renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Models" })
2194
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(
2195
+ TextField3,
2196
+ {
2197
+ ...params,
2198
+ label: "Models",
2199
+ required: true,
2200
+ 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.)"
2201
+ }
2202
+ )
2141
2203
  }
2142
2204
  ), /* @__PURE__ */ React5.createElement(
2143
2205
  TextField3,
@@ -2147,7 +2209,8 @@ var init_ManageTeamDialog = __esm({
2147
2209
  value: maxBudget,
2148
2210
  onChange: (e) => setMaxBudget(e.target.value),
2149
2211
  disabled: submitting || unlimited,
2150
- helperText: maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 ? `Maximum: $${maxBudgetCeiling}` : void 0,
2212
+ inputProps: { min: 0, max: maxBudgetCeiling },
2213
+ helperText: `Maximum: $${maxBudgetCeiling}`,
2151
2214
  fullWidth: true
2152
2215
  }
2153
2216
  ), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
@@ -2159,41 +2222,78 @@ var init_ManageTeamDialog = __esm({
2159
2222
  ), /* @__PURE__ */ React5.createElement(
2160
2223
  TextField3,
2161
2224
  {
2225
+ select: true,
2162
2226
  label: "Budget Duration",
2163
2227
  value: budgetDuration,
2164
2228
  onChange: (e) => setBudgetDuration(e.target.value),
2165
- placeholder: "30d",
2166
- disabled: submitting,
2229
+ disabled: submitting || unlimited,
2230
+ helperText: "Spend-reset period for the team budget",
2167
2231
  fullWidth: true
2168
- }
2169
- ), showMembers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary" }, "No members yet.") : /* @__PURE__ */ React5.createElement(List, { dense: true, disablePadding: true }, members.map((m) => /* @__PURE__ */ React5.createElement(
2170
- ListItem,
2232
+ },
2233
+ durationOptions.map((o) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
2234
+ ), showMembers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "No members yet.") : /* @__PURE__ */ React5.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 } }, /* @__PURE__ */ React5.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React5.createElement(TableHead2, null, /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, null, "User"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Role"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right", sx: { width: 40 } }))), /* @__PURE__ */ React5.createElement(TableBody2, null, members.map((m) => /* @__PURE__ */ React5.createElement(TableRow2, { key: m.user_id }, /* @__PURE__ */ React5.createElement(TableCell2, null, m.user_email ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2" }, m.user_email), /* @__PURE__ */ React5.createElement(
2235
+ Typography5,
2171
2236
  {
2172
- key: m.user_id,
2173
- disableGutters: true,
2174
- secondaryAction: /* @__PURE__ */ React5.createElement(
2175
- IconButton3,
2176
- {
2177
- edge: "end",
2178
- size: "small",
2179
- "aria-label": `remove ${m.user_id}`,
2180
- disabled: memberBusy,
2181
- onClick: () => handleRemoveMember(m.user_id)
2182
- },
2183
- /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2184
- )
2237
+ variant: "caption",
2238
+ color: "text.secondary",
2239
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2185
2240
  },
2186
- /* @__PURE__ */ React5.createElement(ListItemText, { primary: m.user_id, secondary: m.role })
2187
- ))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2188
- TextField3,
2241
+ m.user_id
2242
+ )) : /* @__PURE__ */ React5.createElement(
2243
+ Typography5,
2189
2244
  {
2190
- label: "Backstage user ref",
2191
- placeholder: "user:default/alice",
2192
- value: memberRef,
2193
- onChange: (e) => setMemberRef(e.target.value),
2194
- disabled: memberBusy,
2245
+ variant: "body2",
2246
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2247
+ },
2248
+ m.user_id
2249
+ )), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React5.createElement(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false })), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, /* @__PURE__ */ React5.createElement(
2250
+ IconButton3,
2251
+ {
2252
+ edge: "end",
2195
2253
  size: "small",
2196
- sx: { flex: 1 }
2254
+ "aria-label": `remove ${m.user_id}`,
2255
+ disabled: memberBusy,
2256
+ onClick: () => handleRemoveMember(m.user_id)
2257
+ },
2258
+ /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2259
+ ))))))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" } }, /* @__PURE__ */ React5.createElement(
2260
+ Autocomplete3,
2261
+ {
2262
+ freeSolo: true,
2263
+ autoHighlight: true,
2264
+ options: userOptions,
2265
+ loading: usersLoading,
2266
+ inputValue: memberQuery,
2267
+ onInputChange: (_, newInputValue, reason) => {
2268
+ setMemberQuery(newInputValue);
2269
+ if (reason === "input" || reason === "clear") {
2270
+ setMemberRef(newInputValue);
2271
+ }
2272
+ },
2273
+ onChange: (_, newValue) => {
2274
+ if (newValue && typeof newValue !== "string") {
2275
+ setMemberQuery(newValue.label);
2276
+ setMemberRef(newValue.ref);
2277
+ }
2278
+ },
2279
+ getOptionLabel: (o) => typeof o === "string" ? o : o.label,
2280
+ isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
2281
+ disabled: memberBusy,
2282
+ sx: { flex: 1, minWidth: 260 },
2283
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(
2284
+ TextField3,
2285
+ {
2286
+ ...params,
2287
+ label: "Add member",
2288
+ placeholder: "Search by name or email\u2026",
2289
+ size: "small",
2290
+ helperText: "Pick a catalog user, or paste a user entity ref",
2291
+ InputProps: {
2292
+ ...params.InputProps,
2293
+ endAdornment: /* @__PURE__ */ React5.createElement(React5.Fragment, null, usersLoading ? /* @__PURE__ */ React5.createElement(CircularProgress3, { color: "inherit", size: 14 }) : null, params.InputProps.endAdornment)
2294
+ }
2295
+ }
2296
+ )
2197
2297
  }
2198
2298
  ), /* @__PURE__ */ React5.createElement(
2199
2299
  TextField3,
@@ -2259,19 +2359,19 @@ var init_ManageTeamDialog = __esm({
2259
2359
  });
2260
2360
 
2261
2361
  // src/components/UsageStats.tsx
2262
- import React6, { useMemo as useMemo4, useState as useState4 } from "react";
2263
- import Paper3 from "@mui/material/Paper";
2362
+ import React6, { useMemo as useMemo5, useState as useState4 } from "react";
2363
+ import Paper4 from "@mui/material/Paper";
2264
2364
  import Box6 from "@mui/material/Box";
2265
2365
  import Typography6 from "@mui/material/Typography";
2266
2366
  import TextField4 from "@mui/material/TextField";
2267
- import MenuItem2 from "@mui/material/MenuItem";
2367
+ import MenuItem3 from "@mui/material/MenuItem";
2268
2368
  import Grid from "@mui/material/Grid";
2269
- import Table2 from "@mui/material/Table";
2270
- import TableBody2 from "@mui/material/TableBody";
2271
- import TableCell2 from "@mui/material/TableCell";
2272
- import TableContainer2 from "@mui/material/TableContainer";
2273
- import TableHead2 from "@mui/material/TableHead";
2274
- import TableRow2 from "@mui/material/TableRow";
2369
+ import Table3 from "@mui/material/Table";
2370
+ import TableBody3 from "@mui/material/TableBody";
2371
+ import TableCell3 from "@mui/material/TableCell";
2372
+ import TableContainer3 from "@mui/material/TableContainer";
2373
+ import TableHead3 from "@mui/material/TableHead";
2374
+ import TableRow3 from "@mui/material/TableRow";
2275
2375
  import Skeleton3 from "@mui/material/Skeleton";
2276
2376
  import {
2277
2377
  AreaChart,
@@ -2352,7 +2452,7 @@ var init_UsageStats = __esm({
2352
2452
  }
2353
2453
  onDateRangeChange({ start, end }, preset);
2354
2454
  };
2355
- const dailyData = useMemo4(
2455
+ const dailyData = useMemo5(
2356
2456
  () => (usage?.daily_usage ?? []).map((d) => ({
2357
2457
  date: d.date,
2358
2458
  spend: d.spend,
@@ -2365,21 +2465,21 @@ var init_UsageStats = __esm({
2365
2465
  })),
2366
2466
  [usage]
2367
2467
  );
2368
- const cumulativeData = useMemo4(() => {
2468
+ const cumulativeData = useMemo5(() => {
2369
2469
  let cum = 0;
2370
2470
  return dailyData.map((d) => {
2371
2471
  cum += d.spend;
2372
2472
  return { date: d.date, cumulative: cum };
2373
2473
  });
2374
2474
  }, [dailyData]);
2375
- const successRateData = useMemo4(
2475
+ const successRateData = useMemo5(
2376
2476
  () => dailyData.filter((d) => d.apiRequests > 0).map((d) => ({
2377
2477
  date: d.date,
2378
2478
  successRate: parseFloat((d.successfulRequests / d.apiRequests * 100).toFixed(1))
2379
2479
  })),
2380
2480
  [dailyData]
2381
2481
  );
2382
- const { modelSpendByDate, topModels: topSpendModels } = useMemo4(() => {
2482
+ const { modelSpendByDate, topModels: topSpendModels } = useMemo5(() => {
2383
2483
  const rows = usage?.daily_by_model ?? [];
2384
2484
  const modelTotals = {};
2385
2485
  for (const r of rows) modelTotals[r.model] = (modelTotals[r.model] ?? 0) + r.spend;
@@ -2394,7 +2494,7 @@ var init_UsageStats = __esm({
2394
2494
  const hasOther = sorted.some((r) => r.Other > 0);
2395
2495
  return { modelSpendByDate: sorted, topModels: hasOther ? [...top, "Other"] : top };
2396
2496
  }, [usage]);
2397
- const modelRows = useMemo4(() => {
2497
+ const modelRows = useMemo5(() => {
2398
2498
  const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
2399
2499
  model,
2400
2500
  spend: d.total_spend,
@@ -2408,11 +2508,11 @@ var init_UsageStats = __esm({
2408
2508
  }));
2409
2509
  return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
2410
2510
  }, [usage, selectedModel]);
2411
- const topModelSpendBars = useMemo4(
2511
+ const topModelSpendBars = useMemo5(
2412
2512
  () => [...modelRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2413
2513
  [modelRows]
2414
2514
  );
2415
- const keyRows = useMemo4(
2515
+ const keyRows = useMemo5(
2416
2516
  () => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
2417
2517
  keyHash,
2418
2518
  keyAlias: d.key_alias ?? keyHash.slice(0, 8),
@@ -2429,7 +2529,7 @@ var init_UsageStats = __esm({
2429
2529
  })),
2430
2530
  [usage]
2431
2531
  );
2432
- const topKeySpendBars = useMemo4(
2532
+ const topKeySpendBars = useMemo5(
2433
2533
  () => [...keyRows].sort((a, b) => b.spend - a.spend).slice(0, 10),
2434
2534
  [keyRows]
2435
2535
  );
@@ -2467,21 +2567,21 @@ var init_UsageStats = __esm({
2467
2567
  ];
2468
2568
  const renderKeyRows = () => {
2469
2569
  if (loading) {
2470
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2570
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2471
2571
  }
2472
2572
  if (keyRows.length === 0) {
2473
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2573
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2474
2574
  }
2475
- return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React6.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2575
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React6.createElement(TableRow3, { key: r.keyHash }, /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React6.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2476
2576
  };
2477
2577
  const renderModelRows = () => {
2478
2578
  if (loading) {
2479
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2579
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2480
2580
  }
2481
2581
  if (modelRows.length === 0) {
2482
- return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2582
+ return /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2483
2583
  }
2484
- return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2584
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React6.createElement(TableRow3, { key: r.model }, /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React6.createElement(TableCell3, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2485
2585
  };
2486
2586
  const periodSelect = /* @__PURE__ */ React6.createElement(
2487
2587
  TextField4,
@@ -2493,7 +2593,7 @@ var init_UsageStats = __esm({
2493
2593
  onChange: (e) => handlePresetChange(e.target.value),
2494
2594
  sx: { minWidth: 160 }
2495
2595
  },
2496
- Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: p, value: p }, PRESET_LABELS[p]))
2596
+ Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: p, value: p }, PRESET_LABELS[p]))
2497
2597
  );
2498
2598
  return /* @__PURE__ */ React6.createElement(
2499
2599
  SectionCard,
@@ -2538,8 +2638,8 @@ var init_UsageStats = __esm({
2538
2638
  onChange: (e) => setSelectedModel(e.target.value),
2539
2639
  sx: { minWidth: 220 }
2540
2640
  },
2541
- /* @__PURE__ */ React6.createElement(MenuItem2, { value: "all" }, "All models"),
2542
- models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
2641
+ /* @__PURE__ */ React6.createElement(MenuItem3, { value: "all" }, "All models"),
2642
+ models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: m.model_name, value: m.model_name }, m.model_name))
2543
2643
  )
2544
2644
  ),
2545
2645
  tab === "costs" && /* @__PURE__ */ React6.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ React6.createElement(
@@ -2634,7 +2734,7 @@ var init_UsageStats = __esm({
2634
2734
  height: Math.max(200, topModelSpendBars.length * 34)
2635
2735
  },
2636
2736
  /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2637
- ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Input"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Output"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderModelRows()))))),
2737
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead3, null, /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, null, "Model"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Success"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Input"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Output"), /* @__PURE__ */ React6.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody3, null, renderModelRows()))))),
2638
2738
  tab === "keys" && /* @__PURE__ */ React6.createElement(Grid, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ React6.createElement(
2639
2739
  ChartOrFallback,
2640
2740
  {
@@ -2643,7 +2743,7 @@ var init_UsageStats = __esm({
2643
2743
  height: Math.max(160, topKeySpendBars.length * 32)
2644
2744
  },
2645
2745
  /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2646
- ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React6.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderKeyRows())))))
2746
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer3, { component: Paper4, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead3, null, /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, null, "Key"), /* @__PURE__ */ React6.createElement(TableCell3, null, "Models"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Tokens"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell3, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody3, null, renderKeyRows())))))
2647
2747
  );
2648
2748
  };
2649
2749
  }
@@ -2651,23 +2751,23 @@ var init_UsageStats = __esm({
2651
2751
 
2652
2752
  // src/components/TeamUsage.tsx
2653
2753
  import React7, { useState as useState5 } from "react";
2654
- import Paper4 from "@mui/material/Paper";
2754
+ import Paper5 from "@mui/material/Paper";
2655
2755
  import Box7 from "@mui/material/Box";
2656
2756
  import Typography7 from "@mui/material/Typography";
2657
2757
  import Skeleton4 from "@mui/material/Skeleton";
2658
2758
  import Divider3 from "@mui/material/Divider";
2659
- import Table3 from "@mui/material/Table";
2660
- import TableBody3 from "@mui/material/TableBody";
2661
- import TableCell3 from "@mui/material/TableCell";
2662
- import TableHead3 from "@mui/material/TableHead";
2663
- import TableRow3 from "@mui/material/TableRow";
2664
- import TableContainer3 from "@mui/material/TableContainer";
2759
+ import Table4 from "@mui/material/Table";
2760
+ import TableBody4 from "@mui/material/TableBody";
2761
+ import TableCell4 from "@mui/material/TableCell";
2762
+ import TableHead4 from "@mui/material/TableHead";
2763
+ import TableRow4 from "@mui/material/TableRow";
2764
+ import TableContainer4 from "@mui/material/TableContainer";
2665
2765
  import Collapse from "@mui/material/Collapse";
2666
2766
  import IconButton4 from "@mui/material/IconButton";
2667
2767
  import Button5 from "@mui/material/Button";
2668
- import CircularProgress3 from "@mui/material/CircularProgress";
2768
+ import CircularProgress4 from "@mui/material/CircularProgress";
2669
2769
  import { alpha as alpha4 } from "@mui/material/styles";
2670
- import { ExpandMore as ExpandMore2, Group } from "@mui/icons-material";
2770
+ import { ExpandMore as ExpandMore2, Group, Add as Add3 } from "@mui/icons-material";
2671
2771
  import {
2672
2772
  AreaChart as AreaChart2,
2673
2773
  Area as Area2,
@@ -2699,7 +2799,7 @@ var init_TeamUsage = __esm({
2699
2799
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
2700
2800
  const renderDailySpendSection = () => {
2701
2801
  if (usageLoading) {
2702
- return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress3, { size: 22 }));
2802
+ return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress4, { size: 22 }));
2703
2803
  }
2704
2804
  if (dailyData.length === 0) return null;
2705
2805
  return /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, /* @__PURE__ */ React7.createElement(
@@ -2738,7 +2838,7 @@ var init_TeamUsage = __esm({
2738
2838
  },
2739
2839
  label
2740
2840
  );
2741
- return /* @__PURE__ */ React7.createElement(Paper4, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2841
+ return /* @__PURE__ */ React7.createElement(Paper5, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2742
2842
  Box7,
2743
2843
  {
2744
2844
  sx: (theme) => ({
@@ -2803,13 +2903,13 @@ var init_TeamUsage = __esm({
2803
2903
  /* @__PURE__ */ React7.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2804
2904
  /* @__PURE__ */ React7.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2805
2905
  ), budget > 0 && /* @__PURE__ */ React7.createElement(Box7, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React7.createElement(Typography7, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React7.createElement(Meter, { value: budgetPct, tone: budgetTone2(isOver, isNear), height: 5 })), /* @__PURE__ */ React7.createElement(Collapse, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React7.createElement(Divider3, { sx: { my: 2.5 } }), /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React7.createElement(Box7, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React7.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React7.createElement(Box7, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React7.createElement(
2806
- TableContainer3,
2906
+ TableContainer4,
2807
2907
  {
2808
- component: Paper4,
2908
+ component: Paper5,
2809
2909
  variant: "outlined",
2810
2910
  sx: { borderRadius: 1.5 }
2811
2911
  },
2812
- /* @__PURE__ */ React7.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead3, null, /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, null, "User"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2912
+ /* @__PURE__ */ React7.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead4, null, /* @__PURE__ */ React7.createElement(TableRow4, null, /* @__PURE__ */ React7.createElement(TableCell4, null, "User"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody4, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow4, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell4, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2813
2913
  Typography7,
2814
2914
  {
2815
2915
  variant: "caption",
@@ -2824,7 +2924,7 @@ var init_TeamUsage = __esm({
2824
2924
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2825
2925
  },
2826
2926
  m.user_id
2827
- )), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React7.createElement(
2927
+ )), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(
2828
2928
  StatusPill,
2829
2929
  {
2830
2930
  label: m.role,
@@ -2840,13 +2940,26 @@ var init_TeamUsage = __esm({
2840
2940
  getTeamUsage,
2841
2941
  getTeamUsageLoading,
2842
2942
  canManage,
2843
- onEditTeam
2943
+ onEditTeam,
2944
+ canCreate,
2945
+ onCreateTeam
2844
2946
  }) => {
2947
+ const createAction = canCreate && onCreateTeam ? /* @__PURE__ */ React7.createElement(
2948
+ Button5,
2949
+ {
2950
+ variant: "contained",
2951
+ color: "primary",
2952
+ disableElevation: true,
2953
+ startIcon: /* @__PURE__ */ React7.createElement(Add3, null),
2954
+ onClick: onCreateTeam
2955
+ },
2956
+ "Create Team"
2957
+ ) : void 0;
2845
2958
  if (loading) {
2846
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2959
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2847
2960
  }
2848
2961
  if (!teams.length) {
2849
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(
2962
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", actions: createAction }, /* @__PURE__ */ React7.createElement(
2850
2963
  EmptyState,
2851
2964
  {
2852
2965
  message: "You're not a member of any LiteLLM team yet.",
@@ -2854,32 +2967,40 @@ var init_TeamUsage = __esm({
2854
2967
  }
2855
2968
  ));
2856
2969
  }
2857
- return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}` }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
2858
- TeamCard,
2970
+ return /* @__PURE__ */ React7.createElement(
2971
+ SectionCard,
2859
2972
  {
2860
- key: team.team_id,
2861
- team,
2862
- usage: getTeamUsage(team.team_id),
2863
- usageLoading: getTeamUsageLoading(team.team_id),
2864
- canManage,
2865
- onEditTeam
2866
- }
2867
- ))));
2973
+ title: "Teams",
2974
+ subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}`,
2975
+ actions: createAction
2976
+ },
2977
+ /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
2978
+ TeamCard,
2979
+ {
2980
+ key: team.team_id,
2981
+ team,
2982
+ usage: getTeamUsage(team.team_id),
2983
+ usageLoading: getTeamUsageLoading(team.team_id),
2984
+ canManage,
2985
+ onEditTeam
2986
+ }
2987
+ )))
2988
+ );
2868
2989
  };
2869
2990
  }
2870
2991
  });
2871
2992
 
2872
2993
  // src/components/ModelsTable.tsx
2873
- import React8, { useState as useState6, useMemo as useMemo5, useCallback } from "react";
2994
+ import React8, { useState as useState6, useMemo as useMemo6, useCallback } from "react";
2874
2995
  import Box8 from "@mui/material/Box";
2875
- import Table4 from "@mui/material/Table";
2876
- import TableBody4 from "@mui/material/TableBody";
2877
- import TableCell4 from "@mui/material/TableCell";
2878
- import TableContainer4 from "@mui/material/TableContainer";
2879
- import TableHead4 from "@mui/material/TableHead";
2880
- import TableRow4 from "@mui/material/TableRow";
2996
+ import Table5 from "@mui/material/Table";
2997
+ import TableBody5 from "@mui/material/TableBody";
2998
+ import TableCell5 from "@mui/material/TableCell";
2999
+ import TableContainer5 from "@mui/material/TableContainer";
3000
+ import TableHead5 from "@mui/material/TableHead";
3001
+ import TableRow5 from "@mui/material/TableRow";
2881
3002
  import Typography8 from "@mui/material/Typography";
2882
- import MenuItem3 from "@mui/material/MenuItem";
3003
+ import MenuItem4 from "@mui/material/MenuItem";
2883
3004
  import Select from "@mui/material/Select";
2884
3005
  import FormControl from "@mui/material/FormControl";
2885
3006
  import InputLabel from "@mui/material/InputLabel";
@@ -2915,11 +3036,11 @@ var init_ModelsTable = __esm({
2915
3036
  const theme = useTheme2();
2916
3037
  const [selectedTeamId, setSelectedTeamId] = useState6("");
2917
3038
  const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
2918
- const selectedTeam = useMemo5(
3039
+ const selectedTeam = useMemo6(
2919
3040
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
2920
3041
  [teams, selectedTeamId]
2921
3042
  );
2922
- const filteredModels = useMemo5(() => {
3043
+ const filteredModels = useMemo6(() => {
2923
3044
  if (!selectedTeam) return [];
2924
3045
  return allModels.filter((model) => isModelAllowedByTeam2(model, selectedTeam.models));
2925
3046
  }, [allModels, selectedTeam]);
@@ -2954,7 +3075,7 @@ var init_ModelsTable = __esm({
2954
3075
  label: "Team",
2955
3076
  onChange: (e) => setSelectedTeamId(e.target.value)
2956
3077
  },
2957
- teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
3078
+ teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2958
3079
  ))),
2959
3080
  loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
2960
3081
  !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
@@ -2971,7 +3092,7 @@ var init_ModelsTable = __esm({
2971
3092
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
2972
3093
  }
2973
3094
  ),
2974
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer4, null, /* @__PURE__ */ React8.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead4, null, /* @__PURE__ */ React8.createElement(TableRow4, null, /* @__PURE__ */ React8.createElement(TableCell4, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody4, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow4, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell4, 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(
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(
2975
3096
  Box8,
2976
3097
  {
2977
3098
  component: "span",
@@ -2983,7 +3104,7 @@ var init_ModelsTable = __esm({
2983
3104
  style: { cursor: "pointer", display: "inline-flex" }
2984
3105
  },
2985
3106
  /* @__PURE__ */ React8.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
2986
- )))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(
3107
+ )))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(
2987
3108
  Box8,
2988
3109
  {
2989
3110
  component: "span",
@@ -3000,7 +3121,7 @@ var init_ModelsTable = __esm({
3000
3121
  }
3001
3122
  },
3002
3123
  m.mode
3003
- )), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3124
+ )), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3004
3125
  /* @__PURE__ */ React8.createElement(
3005
3126
  Snackbar,
3006
3127
  {
@@ -3020,22 +3141,22 @@ var init_ModelsTable = __esm({
3020
3141
  import React9, { useState as useState7, useCallback as useCallback2 } from "react";
3021
3142
  import Box9 from "@mui/material/Box";
3022
3143
  import Typography9 from "@mui/material/Typography";
3023
- import Table5 from "@mui/material/Table";
3024
- import TableBody5 from "@mui/material/TableBody";
3025
- import TableCell5 from "@mui/material/TableCell";
3026
- import TableContainer5 from "@mui/material/TableContainer";
3027
- import TableHead5 from "@mui/material/TableHead";
3028
- import TableRow5 from "@mui/material/TableRow";
3144
+ import Table6 from "@mui/material/Table";
3145
+ import TableBody6 from "@mui/material/TableBody";
3146
+ import TableCell6 from "@mui/material/TableCell";
3147
+ import TableContainer6 from "@mui/material/TableContainer";
3148
+ import TableHead6 from "@mui/material/TableHead";
3149
+ import TableRow6 from "@mui/material/TableRow";
3029
3150
  import TablePagination from "@mui/material/TablePagination";
3030
3151
  import TextField5 from "@mui/material/TextField";
3031
- import MenuItem4 from "@mui/material/MenuItem";
3152
+ import MenuItem5 from "@mui/material/MenuItem";
3032
3153
  import Skeleton5 from "@mui/material/Skeleton";
3033
3154
  import Collapse2 from "@mui/material/Collapse";
3034
3155
  import IconButton5 from "@mui/material/IconButton";
3035
3156
  import Alert4 from "@mui/material/Alert";
3036
3157
  import { alpha as alpha6 } from "@mui/material/styles";
3037
3158
  import { KeyboardArrowDown } from "@mui/icons-material";
3038
- import { useAsync } from "react-use";
3159
+ import { useAsync as useAsync2 } from "react-use";
3039
3160
  function actionTone(action) {
3040
3161
  if (!action) return "neutral";
3041
3162
  for (const [key, tone] of Object.entries(ACTION_TONES)) {
@@ -3056,10 +3177,10 @@ function formatDateTime(iso) {
3056
3177
  }
3057
3178
  function renderAuditLogBody(loading, entries) {
3058
3179
  if (loading) {
3059
- return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3180
+ return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3060
3181
  }
3061
3182
  if (entries.length === 0) {
3062
- return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
3183
+ return /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
3063
3184
  EmptyState,
3064
3185
  {
3065
3186
  message: "No audit events found",
@@ -3091,7 +3212,7 @@ var init_AuditLog = __esm({
3091
3212
  DetailRow = ({ entry }) => {
3092
3213
  const [open, setOpen] = useState7(false);
3093
3214
  const hasDetail = entry.before_value || entry.updated_values;
3094
- return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3215
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3095
3216
  IconButton5,
3096
3217
  {
3097
3218
  size: "small",
@@ -3105,7 +3226,7 @@ var init_AuditLog = __esm({
3105
3226
  })
3106
3227
  },
3107
3228
  /* @__PURE__ */ React9.createElement(KeyboardArrowDown, { fontSize: "small" })
3108
- )), /* @__PURE__ */ React9.createElement(TableCell5, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(
3229
+ )), /* @__PURE__ */ React9.createElement(TableCell6, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell6, null, /* @__PURE__ */ React9.createElement(
3109
3230
  Typography9,
3110
3231
  {
3111
3232
  variant: "body2",
@@ -3115,7 +3236,7 @@ var init_AuditLog = __esm({
3115
3236
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
3116
3237
  },
3117
3238
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
3118
- )), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow5, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3239
+ )), /* @__PURE__ */ React9.createElement(TableCell6, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow6, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell6, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3119
3240
  Box9,
3120
3241
  {
3121
3242
  display: "flex",
@@ -3160,7 +3281,7 @@ var init_AuditLog = __esm({
3160
3281
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
3161
3282
  [page, pageSize, filters]
3162
3283
  )();
3163
- const { value, loading, error } = useAsync(
3284
+ const { value, loading, error } = useAsync2(
3164
3285
  () => api.getAuditLogs(fetchParams),
3165
3286
  [api, fetchParams]
3166
3287
  );
@@ -3185,11 +3306,11 @@ var init_AuditLog = __esm({
3185
3306
  },
3186
3307
  sx: { minWidth: 150 }
3187
3308
  },
3188
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All actions"),
3189
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "created" }, "Created"),
3190
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "updated" }, "Updated"),
3191
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "deleted" }, "Deleted"),
3192
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "blocked" }, "Blocked")
3309
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All actions"),
3310
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "created" }, "Created"),
3311
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "updated" }, "Updated"),
3312
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3313
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3193
3314
  ), /* @__PURE__ */ React9.createElement(
3194
3315
  TextField5,
3195
3316
  {
@@ -3203,12 +3324,12 @@ var init_AuditLog = __esm({
3203
3324
  },
3204
3325
  sx: { minWidth: 150 }
3205
3326
  },
3206
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All tables"),
3207
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_VerificationToken" }, "Key"),
3208
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamTable" }, "Team"),
3209
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamMembership" }, "Team member"),
3210
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3211
- /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_UserTable" }, "User")
3327
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All tables"),
3328
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3329
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3330
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3331
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3332
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3212
3333
  ), /* @__PURE__ */ React9.createElement(
3213
3334
  TextField5,
3214
3335
  {
@@ -3224,7 +3345,7 @@ var init_AuditLog = __esm({
3224
3345
  ))
3225
3346
  },
3226
3347
  error && /* @__PURE__ */ React9.createElement(Box9, { px: 2.5, pb: 2 }, /* @__PURE__ */ React9.createElement(Alert4, { severity: "error" }, error.message)),
3227
- /* @__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, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell5, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody5, null, renderAuditLogBody(loading, entries)))),
3348
+ /* @__PURE__ */ React9.createElement(TableContainer6, null, /* @__PURE__ */ React9.createElement(Table6, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead6, null, /* @__PURE__ */ React9.createElement(TableRow6, null, /* @__PURE__ */ React9.createElement(TableCell6, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell6, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell6, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody6, null, renderAuditLogBody(loading, entries)))),
3228
3349
  /* @__PURE__ */ React9.createElement(
3229
3350
  TablePagination,
3230
3351
  {
@@ -3280,18 +3401,17 @@ var LiteLLMPage_exports = {};
3280
3401
  __export(LiteLLMPage_exports, {
3281
3402
  LiteLLMPage: () => LiteLLMPage
3282
3403
  });
3283
- import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo6 } from "react";
3404
+ import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo7 } from "react";
3284
3405
  import Box10 from "@mui/material/Box";
3285
3406
  import Snackbar2 from "@mui/material/Snackbar";
3286
3407
  import Alert5 from "@mui/material/Alert";
3287
- import CircularProgress4 from "@mui/material/CircularProgress";
3408
+ import CircularProgress5 from "@mui/material/CircularProgress";
3288
3409
  import Typography10 from "@mui/material/Typography";
3289
- import Paper5 from "@mui/material/Paper";
3410
+ import Paper6 from "@mui/material/Paper";
3290
3411
  import Tabs2 from "@mui/material/Tabs";
3291
3412
  import Tab2 from "@mui/material/Tab";
3292
- import Button6 from "@mui/material/Button";
3293
- import { useAsync as useAsync2, useAsyncRetry } from "react-use";
3294
- import { useApi } from "@backstage/core-plugin-api";
3413
+ import { useAsync as useAsync3, useAsyncRetry } from "react-use";
3414
+ import { useApi as useApi2 } from "@backstage/core-plugin-api";
3295
3415
  import { usePermission } from "@backstage/plugin-permission-react";
3296
3416
  function initDateRange() {
3297
3417
  let preset = "7d";
@@ -3323,7 +3443,7 @@ var init_LiteLLMPage = __esm({
3323
3443
  init_permissions();
3324
3444
  PERIOD_LS_KEY2 = "litellm_usage_period";
3325
3445
  LiteLLMPage = () => {
3326
- const api = useApi(liteLlmApiRef);
3446
+ const api = useApi2(liteLlmApiRef);
3327
3447
  const [dateRange, setDateRange] = useState8(initDateRange);
3328
3448
  const [currentPreset, setCurrentPreset] = useState8(() => {
3329
3449
  try {
@@ -3338,7 +3458,7 @@ var init_LiteLLMPage = __esm({
3338
3458
  const [manageTeam, setManageTeam] = useState8(null);
3339
3459
  const [teamUsageCache, setTeamUsageCache] = useState8({});
3340
3460
  const [teamUsageLoading, setTeamUsageLoading] = useState8({});
3341
- const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
3461
+ const { value: userInfo, loading: userLoading, error: userError } = useAsync3(
3342
3462
  () => api.getUserInfo(),
3343
3463
  [api]
3344
3464
  );
@@ -3353,7 +3473,7 @@ var init_LiteLLMPage = __esm({
3353
3473
  },
3354
3474
  [api]
3355
3475
  );
3356
- const { value: allModels, loading: modelsLoading } = useAsync2(
3476
+ const { value: allModels, loading: modelsLoading } = useAsync3(
3357
3477
  () => api.listModels().catch(() => []),
3358
3478
  [api]
3359
3479
  );
@@ -3361,8 +3481,8 @@ var init_LiteLLMPage = __esm({
3361
3481
  () => api.getTeams().catch(() => []),
3362
3482
  [api]
3363
3483
  );
3364
- const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
3365
- const { value: managedTeams } = useAsync2(
3484
+ const { value: liteLlmConfig } = useAsync3(() => api.getConfig(), [api]);
3485
+ const { value: managedTeams } = useAsync3(
3366
3486
  async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
3367
3487
  [api, liteLlmConfig]
3368
3488
  );
@@ -3373,15 +3493,15 @@ var init_LiteLLMPage = __esm({
3373
3493
  const { allowed: canManageMcpServers } = usePermission({ permission: litellmTeamMcpManagePermission });
3374
3494
  const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
3375
3495
  const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
3376
- const { value: vectorStores } = useAsync2(
3496
+ const { value: vectorStores } = useAsync3(
3377
3497
  async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
3378
3498
  [api, objectPermsEnabled]
3379
3499
  );
3380
- const { value: mcpServers } = useAsync2(
3500
+ const { value: mcpServers } = useAsync3(
3381
3501
  async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3382
3502
  [api, objectPermsEnabled]
3383
3503
  );
3384
- const teams = useMemo6(() => {
3504
+ const teams = useMemo7(() => {
3385
3505
  if (!allTeams?.length) return [];
3386
3506
  if (!userInfo) return allTeams;
3387
3507
  const userId = userInfo.user_id;
@@ -3393,7 +3513,7 @@ var init_LiteLLMPage = __esm({
3393
3513
  );
3394
3514
  return byMembership.length > 0 ? byMembership : allTeams;
3395
3515
  }, [allTeams, userInfo]);
3396
- const { value: usage, loading: usageLoading } = useAsync2(async () => {
3516
+ const { value: usage, loading: usageLoading } = useAsync3(async () => {
3397
3517
  const startDate = dateRange.start.toISOString().split("T")[0];
3398
3518
  const endDate = dateRange.end.toISOString().split("T")[0];
3399
3519
  return api.getUsage(startDate, endDate);
@@ -3420,7 +3540,7 @@ var init_LiteLLMPage = __esm({
3420
3540
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
3421
3541
  }
3422
3542
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
3423
- const allowedModels = useMemo6(() => {
3543
+ const allowedModels = useMemo7(() => {
3424
3544
  if (!allModels?.length) return [];
3425
3545
  const userModels = userInfo?.models;
3426
3546
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -3528,12 +3648,12 @@ var init_LiteLLMPage = __esm({
3528
3648
  );
3529
3649
  const isInitialLoading = userLoading && !userInfo;
3530
3650
  if (isInitialLoading) {
3531
- return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress4, null));
3651
+ return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress5, null));
3532
3652
  }
3533
3653
  if (userError || !userInfo) {
3534
3654
  const isProvisioningEnabled = userError?.body?.provisioning === true;
3535
3655
  const hint = userError?.body?.hint;
3536
- return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper5, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { 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.")));
3656
+ return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper6, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { 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.")));
3537
3657
  }
3538
3658
  const pageTabs = /* @__PURE__ */ React10.createElement(
3539
3659
  Tabs2,
@@ -3594,14 +3714,7 @@ var init_LiteLLMPage = __esm({
3594
3714
  for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
3595
3715
  for (const t of teams ?? []) teamsById.set(t.team_id, t);
3596
3716
  const visibleTeams = Array.from(teamsById.values());
3597
- return /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teamMgmtEnabled && canCreateTeam && /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", justifyContent: "flex-end" } }, /* @__PURE__ */ React10.createElement(
3598
- Button6,
3599
- {
3600
- variant: "contained",
3601
- onClick: () => setManageTeam({ mode: "create" })
3602
- },
3603
- "Create Team"
3604
- )), /* @__PURE__ */ React10.createElement(
3717
+ return /* @__PURE__ */ React10.createElement(
3605
3718
  TeamUsage,
3606
3719
  {
3607
3720
  teams: visibleTeams,
@@ -3612,9 +3725,11 @@ var init_LiteLLMPage = __esm({
3612
3725
  },
3613
3726
  getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
3614
3727
  canManage: teamMgmtEnabled && canManageTeam,
3615
- onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
3728
+ onEditTeam: (t) => setManageTeam({ mode: "edit", team: t }),
3729
+ canCreate: teamMgmtEnabled && canCreateTeam,
3730
+ onCreateTeam: () => setManageTeam({ mode: "create" })
3616
3731
  }
3617
- ));
3732
+ );
3618
3733
  })(), activeTab === "models" && /* @__PURE__ */ React10.createElement(
3619
3734
  ModelsTable,
3620
3735
  {
@@ -3755,17 +3870,17 @@ init_TeamUsage();
3755
3870
  init_api();
3756
3871
  init_format();
3757
3872
  import React12, { useState as useState9, useEffect as useEffect3 } from "react";
3758
- import Paper6 from "@mui/material/Paper";
3873
+ import Paper7 from "@mui/material/Paper";
3759
3874
  import Box11 from "@mui/material/Box";
3760
3875
  import Typography11 from "@mui/material/Typography";
3761
3876
  import FormControl2 from "@mui/material/FormControl";
3762
3877
  import Select2 from "@mui/material/Select";
3763
- import MenuItem5 from "@mui/material/MenuItem";
3878
+ import MenuItem6 from "@mui/material/MenuItem";
3764
3879
  import Grid2 from "@mui/material/Grid";
3765
- import CircularProgress5 from "@mui/material/CircularProgress";
3880
+ import CircularProgress6 from "@mui/material/CircularProgress";
3766
3881
  import Alert6 from "@mui/material/Alert";
3767
3882
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
3768
- import { useApi as useApi2 } from "@backstage/core-plugin-api";
3883
+ import { useApi as useApi3 } from "@backstage/core-plugin-api";
3769
3884
  function presetToDateRange(preset) {
3770
3885
  const end = /* @__PURE__ */ new Date();
3771
3886
  const start = /* @__PURE__ */ new Date();
@@ -3783,7 +3898,7 @@ var LiteLLMHomeWidget = ({
3783
3898
  defaultPeriod = "7d",
3784
3899
  title = "LiteLLM Usage"
3785
3900
  }) => {
3786
- const api = useApi2(liteLlmApiRef);
3901
+ const api = useApi3(liteLlmApiRef);
3787
3902
  const [period, setPeriod] = useState9(defaultPeriod);
3788
3903
  const [loading, setLoading] = useState9(true);
3789
3904
  const [usageError, setUsageError] = useState9(null);
@@ -3827,17 +3942,17 @@ var LiteLLMHomeWidget = ({
3827
3942
  spend: d.spend
3828
3943
  }));
3829
3944
  const hasSparkline = dailyData.length > 0;
3830
- return /* @__PURE__ */ React12.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
3945
+ return /* @__PURE__ */ React12.createElement(Paper7, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
3831
3946
  Select2,
3832
3947
  {
3833
3948
  value: period,
3834
3949
  onChange: (e) => setPeriod(e.target.value),
3835
3950
  displayEmpty: true
3836
3951
  },
3837
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "today" }, "Today"),
3838
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "7d" }, "7d"),
3839
- /* @__PURE__ */ React12.createElement(MenuItem5, { value: "30d" }, "30d")
3840
- ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress5, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React12.createElement(React12.Fragment, null, partialFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React12.createElement(Box11, { height: 120 }, /* @__PURE__ */ React12.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React12.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React12.createElement(
3952
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "today" }, "Today"),
3953
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "7d" }, "7d"),
3954
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "30d" }, "30d")
3955
+ ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress6, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React12.createElement(React12.Fragment, null, partialFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React12.createElement(Box11, { height: 120 }, /* @__PURE__ */ React12.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React12.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React12.createElement(
3841
3956
  Area3,
3842
3957
  {
3843
3958
  type: "monotone",