@acarmisc/backstage-plugin-litellm 0.15.5 → 0.17.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
@@ -80,6 +80,24 @@ var init_api = __esm({
80
80
  await this.throwIfNotOk(response);
81
81
  return response.json();
82
82
  }
83
+ async patch(path, body) {
84
+ const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {
85
+ method: "PATCH",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify(body)
88
+ });
89
+ await this.throwIfNotOk(response);
90
+ return response.json();
91
+ }
92
+ async put(path, body) {
93
+ const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {
94
+ method: "PUT",
95
+ headers: { "Content-Type": "application/json" },
96
+ body: JSON.stringify(body)
97
+ });
98
+ await this.throwIfNotOk(response);
99
+ return response.json();
100
+ }
83
101
  // User identity is resolved server-side from the Backstage Bearer token.
84
102
  // No user_id param needed on the frontend.
85
103
  async getUserInfo() {
@@ -136,6 +154,44 @@ var init_api = __esm({
136
154
  async getTeams() {
137
155
  return this.get("/teams");
138
156
  }
157
+ // Teams whose owning_group the caller administers (server-scoped). Distinct
158
+ // from getTeams(), which returns only the teams the caller is a member of.
159
+ async getManagedTeams() {
160
+ return this.get("/teams/managed");
161
+ }
162
+ async addTeamMember(teamId, body) {
163
+ return this.post(
164
+ `/teams/${encodeURIComponent(teamId)}/members`,
165
+ body
166
+ );
167
+ }
168
+ async removeTeamMember(teamId, userEntityRef) {
169
+ return this.del(
170
+ `/teams/${encodeURIComponent(teamId)}/members?userEntityRef=${encodeURIComponent(
171
+ userEntityRef
172
+ )}`
173
+ );
174
+ }
175
+ // Vector stores (knowledge bases) the caller may attach — already filtered
176
+ // server-side to the operator's allowlist.
177
+ async getVectorStores() {
178
+ return this.get("/vector-stores");
179
+ }
180
+ async setTeamKnowledgeBases(teamId, vectorStores) {
181
+ return this.put(
182
+ `/teams/${encodeURIComponent(teamId)}/knowledge-bases`,
183
+ { vector_stores: vectorStores }
184
+ );
185
+ }
186
+ async getMcpServers() {
187
+ return this.get("/mcp-servers");
188
+ }
189
+ async setTeamMcpServers(teamId, mcpServers) {
190
+ return this.put(
191
+ `/teams/${encodeURIComponent(teamId)}/mcp-servers`,
192
+ { mcp_servers: mcpServers }
193
+ );
194
+ }
139
195
  async getUsage(startDate, endDate) {
140
196
  return this.get("/usage", { start_date: startDate, end_date: endDate });
141
197
  }
@@ -148,6 +204,12 @@ var init_api = __esm({
148
204
  async getConfig() {
149
205
  return this.get("/config");
150
206
  }
207
+ async createTeam(request) {
208
+ return this.post("/teams", request);
209
+ }
210
+ async updateTeam(teamId, request) {
211
+ return this.patch(`/teams/${encodeURIComponent(teamId)}`, request);
212
+ }
151
213
  };
152
214
  }
153
215
  });
@@ -1857,13 +1919,368 @@ var init_GenerateKeyDialog = __esm({
1857
1919
  }
1858
1920
  });
1859
1921
 
1860
- // src/components/UsageStats.tsx
1861
- import React5, { useMemo as useMemo4, useState as useState3 } from "react";
1862
- import Paper3 from "@mui/material/Paper";
1863
- import Box5 from "@mui/material/Box";
1864
- import Typography5 from "@mui/material/Typography";
1922
+ // src/components/ManageTeamDialog.tsx
1923
+ import React5, { useState as useState3, useEffect as useEffect2 } from "react";
1924
+ import Dialog3 from "@mui/material/Dialog";
1925
+ import DialogTitle3 from "@mui/material/DialogTitle";
1926
+ import DialogContent3 from "@mui/material/DialogContent";
1927
+ import DialogActions3 from "@mui/material/DialogActions";
1865
1928
  import TextField3 from "@mui/material/TextField";
1929
+ import Button4 from "@mui/material/Button";
1930
+ import Alert2 from "@mui/material/Alert";
1931
+ import Checkbox2 from "@mui/material/Checkbox";
1932
+ import FormControlLabel2 from "@mui/material/FormControlLabel";
1933
+ import Autocomplete3 from "@mui/material/Autocomplete";
1866
1934
  import MenuItem2 from "@mui/material/MenuItem";
1935
+ import Box5 from "@mui/material/Box";
1936
+ import Divider2 from "@mui/material/Divider";
1937
+ import Typography5 from "@mui/material/Typography";
1938
+ import IconButton3 from "@mui/material/IconButton";
1939
+ import List from "@mui/material/List";
1940
+ import ListItem from "@mui/material/ListItem";
1941
+ import ListItemText from "@mui/material/ListItemText";
1942
+ import { Delete as DeleteIcon } from "@mui/icons-material";
1943
+ var BUDGET_DURATION_OPTIONS, DEFAULT_BUDGET_DURATION, FALLBACK_BUDGET_CEILING, ManageTeamDialog;
1944
+ var init_ManageTeamDialog = __esm({
1945
+ "src/components/ManageTeamDialog.tsx"() {
1946
+ "use strict";
1947
+ BUDGET_DURATION_OPTIONS = [
1948
+ { value: "1d", label: "Daily (1d)" },
1949
+ { value: "7d", label: "Weekly (7d)" },
1950
+ { value: "30d", label: "Monthly (30d)" },
1951
+ { value: "90d", label: "Quarterly (90d)" },
1952
+ { value: "365d", label: "Yearly (365d)" }
1953
+ ];
1954
+ DEFAULT_BUDGET_DURATION = "30d";
1955
+ FALLBACK_BUDGET_CEILING = 1e3;
1956
+ ManageTeamDialog = ({
1957
+ open,
1958
+ onClose,
1959
+ mode,
1960
+ team,
1961
+ allModels,
1962
+ config,
1963
+ onSubmit,
1964
+ canManageMembers,
1965
+ onAddMember,
1966
+ onRemoveMember,
1967
+ canManageKnowledgeBases,
1968
+ vectorStores,
1969
+ onSaveKnowledgeBases,
1970
+ canManageMcpServers,
1971
+ mcpServers,
1972
+ onSaveMcpServers
1973
+ }) => {
1974
+ const [alias, setAlias] = useState3("");
1975
+ const [models, setModels] = useState3([]);
1976
+ const [maxBudget, setMaxBudget] = useState3("");
1977
+ const [unlimited, setUnlimited] = useState3(false);
1978
+ const [budgetDuration, setBudgetDuration] = useState3("");
1979
+ const [submitting, setSubmitting] = useState3(false);
1980
+ const [error, setError] = useState3(null);
1981
+ const [memberRef, setMemberRef] = useState3("");
1982
+ const [memberBudget, setMemberBudget] = useState3("");
1983
+ const [memberBusy, setMemberBusy] = useState3(false);
1984
+ const [memberError, setMemberError] = useState3(null);
1985
+ const [kbIds, setKbIds] = useState3([]);
1986
+ const [kbBusy, setKbBusy] = useState3(false);
1987
+ const [kbError, setKbError] = useState3(null);
1988
+ const [mcpIds, setMcpIds] = useState3([]);
1989
+ const [mcpBusy, setMcpBusy] = useState3(false);
1990
+ const [mcpError, setMcpError] = useState3(null);
1991
+ const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
1992
+ const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
1993
+ useEffect2(() => {
1994
+ if (open) {
1995
+ if (mode === "edit" && team) {
1996
+ setAlias(team.team_alias ?? "");
1997
+ setModels(team.models ?? []);
1998
+ setMaxBudget(
1999
+ team.max_budget ? String(team.max_budget) : String(maxBudgetCeiling)
2000
+ );
2001
+ setUnlimited(allowUnlimitedBudget && !team.max_budget);
2002
+ setBudgetDuration(team.budget_duration ?? DEFAULT_BUDGET_DURATION);
2003
+ } else {
2004
+ setAlias("");
2005
+ setModels([]);
2006
+ setMaxBudget(String(maxBudgetCeiling));
2007
+ setUnlimited(allowUnlimitedBudget);
2008
+ setBudgetDuration(DEFAULT_BUDGET_DURATION);
2009
+ }
2010
+ setError(null);
2011
+ setMemberRef("");
2012
+ setMemberBudget("");
2013
+ setMemberError(null);
2014
+ setKbIds(team?.object_permission?.vector_stores ?? []);
2015
+ setKbError(null);
2016
+ setMcpIds(team?.object_permission?.mcp_servers ?? []);
2017
+ setMcpError(null);
2018
+ }
2019
+ }, [open, mode, team, allowUnlimitedBudget, maxBudgetCeiling]);
2020
+ const handleSubmit = async () => {
2021
+ setError(null);
2022
+ if (!alias.trim()) {
2023
+ setError("Team alias is required");
2024
+ return;
2025
+ }
2026
+ if (models.length === 0) {
2027
+ setError("At least one model is required");
2028
+ return;
2029
+ }
2030
+ if (!unlimited) {
2031
+ if (!maxBudget) {
2032
+ setError("Budget is required when unlimited budgets are disabled");
2033
+ return;
2034
+ }
2035
+ const budget = parseFloat(maxBudget);
2036
+ if (isNaN(budget) || budget <= 0) {
2037
+ setError("Budget must be a positive number");
2038
+ return;
2039
+ }
2040
+ if (budget > maxBudgetCeiling) {
2041
+ setError(`Budget cannot exceed $${maxBudgetCeiling}`);
2042
+ return;
2043
+ }
2044
+ }
2045
+ try {
2046
+ setSubmitting(true);
2047
+ let payload;
2048
+ if (mode === "create") {
2049
+ payload = {
2050
+ team_alias: alias.trim(),
2051
+ models,
2052
+ ...unlimited ? { max_budget: null } : { max_budget: parseFloat(maxBudget) },
2053
+ ...budgetDuration && { budget_duration: budgetDuration }
2054
+ };
2055
+ } else {
2056
+ payload = {
2057
+ team_alias: alias.trim(),
2058
+ models,
2059
+ ...unlimited ? { max_budget: null } : { max_budget: parseFloat(maxBudget) },
2060
+ ...budgetDuration && { budget_duration: budgetDuration }
2061
+ };
2062
+ }
2063
+ await onSubmit(payload);
2064
+ onClose();
2065
+ } catch (err) {
2066
+ setError(err instanceof Error ? err.message : "An error occurred");
2067
+ } finally {
2068
+ setSubmitting(false);
2069
+ }
2070
+ };
2071
+ const handleAddMember = async () => {
2072
+ if (!onAddMember || !memberRef.trim()) return;
2073
+ setMemberError(null);
2074
+ const budget = memberBudget ? parseFloat(memberBudget) : void 0;
2075
+ if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
2076
+ setMemberError("Max budget in team must be a positive number");
2077
+ return;
2078
+ }
2079
+ try {
2080
+ setMemberBusy(true);
2081
+ await onAddMember(memberRef.trim(), budget);
2082
+ setMemberRef("");
2083
+ setMemberBudget("");
2084
+ } catch (err) {
2085
+ setMemberError(err instanceof Error ? err.message : "Failed to add member");
2086
+ } finally {
2087
+ setMemberBusy(false);
2088
+ }
2089
+ };
2090
+ const handleRemoveMember = async (userId) => {
2091
+ if (!onRemoveMember) return;
2092
+ setMemberError(null);
2093
+ try {
2094
+ setMemberBusy(true);
2095
+ await onRemoveMember(userId);
2096
+ } catch (err) {
2097
+ setMemberError(err instanceof Error ? err.message : "Failed to remove member");
2098
+ } finally {
2099
+ setMemberBusy(false);
2100
+ }
2101
+ };
2102
+ const handleSaveKnowledgeBases = async () => {
2103
+ if (!onSaveKnowledgeBases) return;
2104
+ setKbError(null);
2105
+ try {
2106
+ setKbBusy(true);
2107
+ await onSaveKnowledgeBases(kbIds);
2108
+ } catch (err) {
2109
+ setKbError(err instanceof Error ? err.message : "Failed to save knowledge bases");
2110
+ } finally {
2111
+ setKbBusy(false);
2112
+ }
2113
+ };
2114
+ const handleSaveMcpServers = async () => {
2115
+ if (!onSaveMcpServers) return;
2116
+ setMcpError(null);
2117
+ try {
2118
+ setMcpBusy(true);
2119
+ await onSaveMcpServers(mcpIds);
2120
+ } catch (err) {
2121
+ setMcpError(err instanceof Error ? err.message : "Failed to save MCP servers");
2122
+ } finally {
2123
+ setMcpBusy(false);
2124
+ }
2125
+ };
2126
+ const modelNames = allModels.map((m) => m.model_name);
2127
+ const durationOptions = BUDGET_DURATION_OPTIONS.some((o) => o.value === budgetDuration) ? BUDGET_DURATION_OPTIONS : [...BUDGET_DURATION_OPTIONS, { value: budgetDuration, label: budgetDuration }];
2128
+ const members = team?.members_with_roles ?? [];
2129
+ const showMembers = mode === "edit" && !!canManageMembers;
2130
+ const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
2131
+ const kbOptions = (vectorStores ?? []).map((v) => v.id);
2132
+ const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
2133
+ const showMcpServers = mode === "edit" && !!canManageMcpServers;
2134
+ const mcpOptions = (mcpServers ?? []).map((s) => s.id);
2135
+ const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
2136
+ return /* @__PURE__ */ React5.createElement(Dialog3, { open, onClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React5.createElement(DialogTitle3, null, mode === "create" ? "Create Team" : "Edit Team"), /* @__PURE__ */ React5.createElement(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 } }, error && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error" }, error), /* @__PURE__ */ React5.createElement(
2137
+ TextField3,
2138
+ {
2139
+ label: "Team Alias",
2140
+ value: alias,
2141
+ onChange: (e) => setAlias(e.target.value),
2142
+ disabled: submitting,
2143
+ fullWidth: true
2144
+ }
2145
+ ), /* @__PURE__ */ React5.createElement(
2146
+ Autocomplete3,
2147
+ {
2148
+ multiple: true,
2149
+ options: modelNames,
2150
+ value: models,
2151
+ onChange: (_, newValue) => setModels(newValue),
2152
+ disabled: submitting,
2153
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Models" })
2154
+ }
2155
+ ), /* @__PURE__ */ React5.createElement(
2156
+ TextField3,
2157
+ {
2158
+ label: "Max Budget ($)",
2159
+ type: "number",
2160
+ value: maxBudget,
2161
+ onChange: (e) => setMaxBudget(e.target.value),
2162
+ disabled: submitting || unlimited,
2163
+ inputProps: { min: 0, max: maxBudgetCeiling },
2164
+ helperText: `Maximum: $${maxBudgetCeiling}`,
2165
+ fullWidth: true
2166
+ }
2167
+ ), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
2168
+ FormControlLabel2,
2169
+ {
2170
+ control: /* @__PURE__ */ React5.createElement(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
2171
+ label: "Unlimited Budget"
2172
+ }
2173
+ ), /* @__PURE__ */ React5.createElement(
2174
+ TextField3,
2175
+ {
2176
+ select: true,
2177
+ label: "Budget Duration",
2178
+ value: budgetDuration,
2179
+ onChange: (e) => setBudgetDuration(e.target.value),
2180
+ disabled: submitting || unlimited,
2181
+ helperText: "Spend-reset period for the team budget",
2182
+ fullWidth: true
2183
+ },
2184
+ durationOptions.map((o) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: o.value, value: o.value }, o.label))
2185
+ ), 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(
2186
+ ListItem,
2187
+ {
2188
+ key: m.user_id,
2189
+ disableGutters: true,
2190
+ secondaryAction: /* @__PURE__ */ React5.createElement(
2191
+ IconButton3,
2192
+ {
2193
+ edge: "end",
2194
+ size: "small",
2195
+ "aria-label": `remove ${m.user_id}`,
2196
+ disabled: memberBusy,
2197
+ onClick: () => handleRemoveMember(m.user_id)
2198
+ },
2199
+ /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2200
+ )
2201
+ },
2202
+ /* @__PURE__ */ React5.createElement(ListItemText, { primary: m.user_id, secondary: m.role })
2203
+ ))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2204
+ TextField3,
2205
+ {
2206
+ label: "Backstage user ref",
2207
+ placeholder: "user:default/alice",
2208
+ value: memberRef,
2209
+ onChange: (e) => setMemberRef(e.target.value),
2210
+ disabled: memberBusy,
2211
+ size: "small",
2212
+ sx: { flex: 1 }
2213
+ }
2214
+ ), /* @__PURE__ */ React5.createElement(
2215
+ TextField3,
2216
+ {
2217
+ label: "Max budget in team ($)",
2218
+ type: "number",
2219
+ value: memberBudget,
2220
+ onChange: (e) => setMemberBudget(e.target.value),
2221
+ disabled: memberBusy,
2222
+ size: "small",
2223
+ sx: { width: 160 }
2224
+ }
2225
+ ), /* @__PURE__ */ React5.createElement(
2226
+ Button4,
2227
+ {
2228
+ onClick: handleAddMember,
2229
+ disabled: memberBusy || !memberRef.trim(),
2230
+ variant: "outlined",
2231
+ sx: { mt: 0.5 }
2232
+ },
2233
+ "Add"
2234
+ ))), showKnowledgeBases && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Knowledge Bases"), /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching a knowledge base exposes its documents to every key in this team."), kbError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setKbError(null) }, kbError), /* @__PURE__ */ React5.createElement(
2235
+ Autocomplete3,
2236
+ {
2237
+ multiple: true,
2238
+ options: kbOptions,
2239
+ value: kbIds,
2240
+ getOptionLabel: kbLabel,
2241
+ onChange: (_, v) => setKbIds(v),
2242
+ disabled: kbBusy,
2243
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Attached knowledge bases" })
2244
+ }
2245
+ ), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2246
+ Button4,
2247
+ {
2248
+ onClick: handleSaveKnowledgeBases,
2249
+ disabled: kbBusy,
2250
+ variant: "outlined"
2251
+ },
2252
+ kbBusy ? "Saving\u2026" : "Save knowledge bases"
2253
+ ))), showMcpServers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "MCP Servers"), /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary", sx: { mb: 1 } }, "Attaching an MCP server lets every key in this team invoke that server's tools through the model \u2014 including any side effects those tools have."), mcpError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMcpError(null) }, mcpError), /* @__PURE__ */ React5.createElement(
2254
+ Autocomplete3,
2255
+ {
2256
+ multiple: true,
2257
+ options: mcpOptions,
2258
+ value: mcpIds,
2259
+ getOptionLabel: mcpLabel,
2260
+ onChange: (_, v) => setMcpIds(v),
2261
+ disabled: mcpBusy,
2262
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Attached MCP servers" })
2263
+ }
2264
+ ), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2265
+ Button4,
2266
+ {
2267
+ onClick: handleSaveMcpServers,
2268
+ disabled: mcpBusy,
2269
+ variant: "outlined"
2270
+ },
2271
+ mcpBusy ? "Saving\u2026" : "Save MCP servers"
2272
+ )))), /* @__PURE__ */ React5.createElement(DialogActions3, null, /* @__PURE__ */ React5.createElement(Button4, { onClick: onClose, disabled: submitting }, "Cancel"), /* @__PURE__ */ React5.createElement(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained" }, submitting ? "Saving..." : "Save")));
2273
+ };
2274
+ }
2275
+ });
2276
+
2277
+ // src/components/UsageStats.tsx
2278
+ import React6, { useMemo as useMemo4, useState as useState4 } from "react";
2279
+ import Paper3 from "@mui/material/Paper";
2280
+ import Box6 from "@mui/material/Box";
2281
+ import Typography6 from "@mui/material/Typography";
2282
+ import TextField4 from "@mui/material/TextField";
2283
+ import MenuItem3 from "@mui/material/MenuItem";
1867
2284
  import Grid from "@mui/material/Grid";
1868
2285
  import Table2 from "@mui/material/Table";
1869
2286
  import TableBody2 from "@mui/material/TableBody";
@@ -1908,17 +2325,17 @@ var init_UsageStats = __esm({
1908
2325
  "30d": "Last 30 days"
1909
2326
  };
1910
2327
  fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
1911
- ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React5.createElement(Skeleton3, { variant: "rounded", height });
2328
+ ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height });
1912
2329
  ChartOrFallback = ({ loading, empty, height = 240, children }) => {
1913
- if (loading) return /* @__PURE__ */ React5.createElement(ChartSkeleton, { height });
1914
- if (empty) return /* @__PURE__ */ React5.createElement(EmptyState, { message: "No data for this period", height });
1915
- return /* @__PURE__ */ React5.createElement(React5.Fragment, null, children);
2330
+ if (loading) return /* @__PURE__ */ React6.createElement(ChartSkeleton, { height });
2331
+ if (empty) return /* @__PURE__ */ React6.createElement(EmptyState, { message: "No data for this period", height });
2332
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, children);
1916
2333
  };
1917
2334
  SuccessRateCell = ({ rate, requests }) => {
1918
- if (requests === 0) return /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary" }, "\u2014");
2335
+ if (requests === 0) return /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "\u2014");
1919
2336
  const tone = rateTone(rate);
1920
- return /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React5.createElement(Box5, { width: 72, flexShrink: 0 }, /* @__PURE__ */ React5.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ React5.createElement(
1921
- Typography5,
2337
+ return /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { width: 72, flexShrink: 0 }, /* @__PURE__ */ React6.createElement(Meter, { value: rate * 100, tone, height: 4 })), /* @__PURE__ */ React6.createElement(
2338
+ Typography6,
1922
2339
  {
1923
2340
  variant: "caption",
1924
2341
  sx: { fontVariantNumeric: "tabular-nums", minWidth: 44 }
@@ -1936,8 +2353,8 @@ var init_UsageStats = __esm({
1936
2353
  userInfo
1937
2354
  }) => {
1938
2355
  const chart = useChartTheme();
1939
- const [selectedModel, setSelectedModel] = useState3("all");
1940
- const [tab, setTab] = useState3("costs");
2356
+ const [selectedModel, setSelectedModel] = useState4("all");
2357
+ const [tab, setTab] = useState4("costs");
1941
2358
  const handlePresetChange = (preset) => {
1942
2359
  const end = /* @__PURE__ */ new Date();
1943
2360
  const start = /* @__PURE__ */ new Date();
@@ -2066,24 +2483,24 @@ var init_UsageStats = __esm({
2066
2483
  ];
2067
2484
  const renderKeyRows = () => {
2068
2485
  if (loading) {
2069
- return /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React5.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2486
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2070
2487
  }
2071
2488
  if (keyRows.length === 0) {
2072
- return /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React5.createElement(EmptyState, { message: "No key activity in this period" })));
2489
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2073
2490
  }
2074
- return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React5.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React5.createElement(TableCell2, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React5.createElement(Typography5, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React5.createElement(TableCell2, null, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React5.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React5.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React5.createElement(TableCell2, null, /* @__PURE__ */ React5.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2491
+ 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 }))));
2075
2492
  };
2076
2493
  const renderModelRows = () => {
2077
2494
  if (loading) {
2078
- return /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React5.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2495
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2079
2496
  }
2080
2497
  if (modelRows.length === 0) {
2081
- return /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React5.createElement(EmptyState, { message: "No model activity in this period" })));
2498
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2082
2499
  }
2083
- return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React5.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React5.createElement(TableCell2, null, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React5.createElement(Box5, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React5.createElement(TableCell2, null, /* @__PURE__ */ React5.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2500
+ 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 }))));
2084
2501
  };
2085
- const periodSelect = /* @__PURE__ */ React5.createElement(
2086
- TextField3,
2502
+ const periodSelect = /* @__PURE__ */ React6.createElement(
2503
+ TextField4,
2087
2504
  {
2088
2505
  select: true,
2089
2506
  size: "small",
@@ -2092,18 +2509,18 @@ var init_UsageStats = __esm({
2092
2509
  onChange: (e) => handlePresetChange(e.target.value),
2093
2510
  sx: { minWidth: 160 }
2094
2511
  },
2095
- Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: p, value: p }, PRESET_LABELS[p]))
2512
+ Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: p, value: p }, PRESET_LABELS[p]))
2096
2513
  );
2097
- return /* @__PURE__ */ React5.createElement(
2514
+ return /* @__PURE__ */ React6.createElement(
2098
2515
  SectionCard,
2099
2516
  {
2100
2517
  title: "Usage Analytics",
2101
2518
  subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
2102
2519
  actions: periodSelect
2103
2520
  },
2104
- /* @__PURE__ */ React5.createElement(MetricStrip, { metrics }),
2105
- /* @__PURE__ */ React5.createElement(
2106
- Box5,
2521
+ /* @__PURE__ */ React6.createElement(MetricStrip, { metrics }),
2522
+ /* @__PURE__ */ React6.createElement(
2523
+ Box6,
2107
2524
  {
2108
2525
  sx: {
2109
2526
  display: "flex",
@@ -2115,7 +2532,7 @@ var init_UsageStats = __esm({
2115
2532
  mb: 2
2116
2533
  }
2117
2534
  },
2118
- /* @__PURE__ */ React5.createElement(
2535
+ /* @__PURE__ */ React6.createElement(
2119
2536
  SegmentedControl,
2120
2537
  {
2121
2538
  value: tab,
@@ -2127,8 +2544,8 @@ var init_UsageStats = __esm({
2127
2544
  ]
2128
2545
  }
2129
2546
  ),
2130
- tab === "models" && /* @__PURE__ */ React5.createElement(
2131
- TextField3,
2547
+ tab === "models" && /* @__PURE__ */ React6.createElement(
2548
+ TextField4,
2132
2549
  {
2133
2550
  select: true,
2134
2551
  size: "small",
@@ -2137,17 +2554,17 @@ var init_UsageStats = __esm({
2137
2554
  onChange: (e) => setSelectedModel(e.target.value),
2138
2555
  sx: { minWidth: 220 }
2139
2556
  },
2140
- /* @__PURE__ */ React5.createElement(MenuItem2, { value: "all" }, "All models"),
2141
- models.map((m) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
2557
+ /* @__PURE__ */ React6.createElement(MenuItem3, { value: "all" }, "All models"),
2558
+ models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem3, { key: m.model_name, value: m.model_name }, m.model_name))
2142
2559
  )
2143
2560
  ),
2144
- tab === "costs" && /* @__PURE__ */ React5.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Daily spend by model", height: 260 }, /* @__PURE__ */ React5.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0, height: 260 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(AreaChart, { data: modelSpendByDate, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { tickFormatter: fmtUsdCompact, width: 56, ...chart.axis }), /* @__PURE__ */ React5.createElement(
2561
+ 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(
2145
2562
  Tooltip,
2146
2563
  {
2147
2564
  cursor: chart.cursor,
2148
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2565
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2149
2566
  }
2150
- ), topSpendModels.map((m) => /* @__PURE__ */ React5.createElement(
2567
+ ), topSpendModels.map((m) => /* @__PURE__ */ React6.createElement(
2151
2568
  Area,
2152
2569
  {
2153
2570
  key: m,
@@ -2159,13 +2576,13 @@ var init_UsageStats = __esm({
2159
2576
  fill: chartColor(m),
2160
2577
  fillOpacity: 0.28
2161
2578
  }
2162
- ))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ React5.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ React5.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(AreaChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React5.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React5.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React5.createElement(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ React5.createElement(Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ React5.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React5.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React5.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ React5.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(LineChart, { data: successRateData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ React5.createElement(
2579
+ ))))), !loading && topSpendModels.length > 0 && /* @__PURE__ */ React6.createElement(SeriesLegend, { series: topSpendModels.map((m) => ({ name: m, color: chartColor(m) })) }))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Daily token usage" }, /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(AreaChart, { data: dailyData, 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: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React6.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React6.createElement(Area, { type: "monotone", dataKey: "promptTokens", name: "Input", stackId: "tok", stroke: SERIES.input, strokeWidth: 1.5, fill: SERIES.input, fillOpacity: 0.25 }), /* @__PURE__ */ React6.createElement(Area, { type: "monotone", dataKey: "completionTokens", name: "Output", stackId: "tok", stroke: SERIES.output, strokeWidth: 1.5, fill: SERIES.output, fillOpacity: 0.25 })))))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Daily requests" }, /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 }, barCategoryGap: "30%" }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React6.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { tickFormatter: fmtCompact, width: 48, ...chart.axis }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtInt }) }), /* @__PURE__ */ React6.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: SERIES.success, stackId: "r" }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: SERIES.failure, stackId: "r", radius: [3, 3, 0, 0] })))))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Daily success rate" }, /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(LineChart, { data: successRateData, 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, { domain: [0, 100], tickFormatter: (v) => `${v}%`, width: 44, ...chart.axis }), /* @__PURE__ */ React6.createElement(
2163
2580
  Tooltip,
2164
2581
  {
2165
2582
  cursor: chart.cursor,
2166
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2583
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2167
2584
  }
2168
- ), /* @__PURE__ */ React5.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React5.createElement(
2585
+ ), /* @__PURE__ */ React6.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React6.createElement(
2169
2586
  Line,
2170
2587
  {
2171
2588
  type: "monotone",
@@ -2176,13 +2593,13 @@ var init_UsageStats = __esm({
2176
2593
  dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
2177
2594
  activeDot: { r: 4 }
2178
2595
  }
2179
- )))))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(
2596
+ )))))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(
2180
2597
  ChartCard,
2181
2598
  {
2182
2599
  title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
2183
2600
  meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
2184
2601
  },
2185
- /* @__PURE__ */ React5.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(AreaChart, { data: cumulativeData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid }), /* @__PURE__ */ React5.createElement(XAxis, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React5.createElement(
2602
+ /* @__PURE__ */ React6.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(AreaChart, { data: cumulativeData, 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(
2186
2603
  YAxis,
2187
2604
  {
2188
2605
  tickFormatter: fmtUsdCompact,
@@ -2190,13 +2607,13 @@ var init_UsageStats = __esm({
2190
2607
  domain: cumulativeDomain,
2191
2608
  ...chart.axis
2192
2609
  }
2193
- ), /* @__PURE__ */ React5.createElement(
2610
+ ), /* @__PURE__ */ React6.createElement(
2194
2611
  Tooltip,
2195
2612
  {
2196
2613
  cursor: chart.cursor,
2197
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2614
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2198
2615
  }
2199
- ), maxBudget > 0 && /* @__PURE__ */ React5.createElement(
2616
+ ), maxBudget > 0 && /* @__PURE__ */ React6.createElement(
2200
2617
  ReferenceLine,
2201
2618
  {
2202
2619
  y: maxBudget,
@@ -2204,7 +2621,7 @@ var init_UsageStats = __esm({
2204
2621
  strokeDasharray: "5 4",
2205
2622
  label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
2206
2623
  }
2207
- ), /* @__PURE__ */ React5.createElement(
2624
+ ), /* @__PURE__ */ React6.createElement(
2208
2625
  Area,
2209
2626
  {
2210
2627
  type: "monotone",
@@ -2217,44 +2634,44 @@ var init_UsageStats = __esm({
2217
2634
  }
2218
2635
  ))))
2219
2636
  ))),
2220
- tab === "models" && /* @__PURE__ */ React5.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React5.createElement(
2637
+ tab === "models" && /* @__PURE__ */ React6.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Spend by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React6.createElement(
2221
2638
  ChartOrFallback,
2222
2639
  {
2223
2640
  loading,
2224
2641
  empty: topModelSpendBars.length === 0,
2225
2642
  height: Math.max(200, topModelSpendBars.length * 34)
2226
2643
  },
2227
- /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React5.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React5.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ React5.createElement(Cell, { key: r.model, fill: chartColor(r.model) })))))
2228
- ))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React5.createElement(
2644
+ /* @__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: fmtUsdCompact, ...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: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "spend", name: "Spend", radius: [0, 3, 3, 0], barSize: 14 }, topModelSpendBars.map((r) => /* @__PURE__ */ React6.createElement(Cell, { key: r.model, fill: chartColor(r.model) })))))
2645
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(ChartCard, { title: "Tokens by model", height: Math.max(200, topModelSpendBars.length * 34) }, /* @__PURE__ */ React6.createElement(
2229
2646
  ChartOrFallback,
2230
2647
  {
2231
2648
  loading,
2232
2649
  empty: modelRows.length === 0,
2233
2650
  height: Math.max(200, topModelSpendBars.length * 34)
2234
2651
  },
2235
- /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React5.createElement(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React5.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ React5.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2236
- ))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React5.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React5.createElement(TableHead2, null, /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Input"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Output"), /* @__PURE__ */ React5.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React5.createElement(TableBody2, null, renderModelRows()))))),
2237
- tab === "keys" && /* @__PURE__ */ React5.createElement(Grid, { container: true, spacing: 2 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(ChartCard, { title: "Spend by key", height: Math.max(160, topKeySpendBars.length * 32) }, /* @__PURE__ */ React5.createElement(
2652
+ /* @__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] })))
2653
+ ))), /* @__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()))))),
2654
+ 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(
2238
2655
  ChartOrFallback,
2239
2656
  {
2240
2657
  loading,
2241
2658
  empty: topKeySpendBars.length === 0,
2242
2659
  height: Math.max(160, topKeySpendBars.length * 32)
2243
2660
  },
2244
- /* @__PURE__ */ React5.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React5.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React5.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React5.createElement(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React5.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React5.createElement(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2245
- ))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React5.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React5.createElement(TableHead2, null, /* @__PURE__ */ React5.createElement(TableRow2, null, /* @__PURE__ */ React5.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React5.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React5.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React5.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React5.createElement(TableBody2, null, renderKeyRows())))))
2661
+ /* @__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 })))
2662
+ ))), /* @__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())))))
2246
2663
  );
2247
2664
  };
2248
2665
  }
2249
2666
  });
2250
2667
 
2251
2668
  // src/components/TeamUsage.tsx
2252
- import React6, { useState as useState4 } from "react";
2669
+ import React7, { useState as useState5 } from "react";
2253
2670
  import Paper4 from "@mui/material/Paper";
2254
- import Box6 from "@mui/material/Box";
2255
- import Typography6 from "@mui/material/Typography";
2671
+ import Box7 from "@mui/material/Box";
2672
+ import Typography7 from "@mui/material/Typography";
2256
2673
  import Skeleton4 from "@mui/material/Skeleton";
2257
- import Divider2 from "@mui/material/Divider";
2674
+ import Divider3 from "@mui/material/Divider";
2258
2675
  import Table3 from "@mui/material/Table";
2259
2676
  import TableBody3 from "@mui/material/TableBody";
2260
2677
  import TableCell3 from "@mui/material/TableCell";
@@ -2262,7 +2679,8 @@ import TableHead3 from "@mui/material/TableHead";
2262
2679
  import TableRow3 from "@mui/material/TableRow";
2263
2680
  import TableContainer3 from "@mui/material/TableContainer";
2264
2681
  import Collapse from "@mui/material/Collapse";
2265
- import IconButton3 from "@mui/material/IconButton";
2682
+ import IconButton4 from "@mui/material/IconButton";
2683
+ import Button5 from "@mui/material/Button";
2266
2684
  import CircularProgress3 from "@mui/material/CircularProgress";
2267
2685
  import { alpha as alpha4 } from "@mui/material/styles";
2268
2686
  import { ExpandMore as ExpandMore2, Group } from "@mui/icons-material";
@@ -2286,8 +2704,8 @@ var init_TeamUsage = __esm({
2286
2704
  "use strict";
2287
2705
  init_ui();
2288
2706
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
2289
- TeamCard = ({ team, usage, usageLoading }) => {
2290
- const [expanded, setExpanded] = useState4(false);
2707
+ TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
2708
+ const [expanded, setExpanded] = useState5(false);
2291
2709
  const chart = useChartTheme();
2292
2710
  const budget = team.max_budget ?? 0;
2293
2711
  const spend = team.spend ?? 0;
@@ -2297,24 +2715,24 @@ var init_TeamUsage = __esm({
2297
2715
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
2298
2716
  const renderDailySpendSection = () => {
2299
2717
  if (usageLoading) {
2300
- return /* @__PURE__ */ React6.createElement(Box6, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React6.createElement(CircularProgress3, { size: 22 }));
2718
+ return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress3, { size: 22 }));
2301
2719
  }
2302
2720
  if (dailyData.length === 0) return null;
2303
- return /* @__PURE__ */ React6.createElement(Box6, { mb: 2.5 }, /* @__PURE__ */ React6.createElement(
2304
- Typography6,
2721
+ return /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, /* @__PURE__ */ React7.createElement(
2722
+ Typography7,
2305
2723
  {
2306
2724
  variant: "caption",
2307
2725
  color: "text.secondary",
2308
2726
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
2309
2727
  },
2310
2728
  "Daily spend"
2311
- ), /* @__PURE__ */ React6.createElement(Box6, { height: 150 }, /* @__PURE__ */ React6.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React6.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React6.createElement(
2729
+ ), /* @__PURE__ */ React7.createElement(Box7, { height: 150 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart2, { data: dailyData, margin: { top: 4, right: 8, left: 0, bottom: 0 } }, /* @__PURE__ */ React7.createElement(CartesianGrid2, { ...chart.grid }), /* @__PURE__ */ React7.createElement(XAxis2, { dataKey: "date", tickFormatter: fmtDateShort, ...chart.axis }), /* @__PURE__ */ React7.createElement(YAxis2, { tickFormatter: fmtUsdCompact, width: 52, ...chart.axis }), /* @__PURE__ */ React7.createElement(
2312
2730
  Tooltip2,
2313
2731
  {
2314
2732
  cursor: chart.cursor,
2315
- content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
2733
+ content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
2316
2734
  }
2317
- ), /* @__PURE__ */ React6.createElement(
2735
+ ), /* @__PURE__ */ React7.createElement(
2318
2736
  Area2,
2319
2737
  {
2320
2738
  type: "monotone",
@@ -2327,8 +2745,8 @@ var init_TeamUsage = __esm({
2327
2745
  }
2328
2746
  )))));
2329
2747
  };
2330
- const sectionLabel = (label) => /* @__PURE__ */ React6.createElement(
2331
- Typography6,
2748
+ const sectionLabel = (label) => /* @__PURE__ */ React7.createElement(
2749
+ Typography7,
2332
2750
  {
2333
2751
  variant: "caption",
2334
2752
  color: "text.secondary",
@@ -2336,8 +2754,8 @@ var init_TeamUsage = __esm({
2336
2754
  },
2337
2755
  label
2338
2756
  );
2339
- return /* @__PURE__ */ React6.createElement(Paper4, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React6.createElement(
2340
- Box6,
2757
+ 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(
2758
+ Box7,
2341
2759
  {
2342
2760
  sx: (theme) => ({
2343
2761
  width: 36,
@@ -2351,17 +2769,26 @@ var init_TeamUsage = __esm({
2351
2769
  color: theme.palette.primary.main
2352
2770
  })
2353
2771
  },
2354
- /* @__PURE__ */ React6.createElement(Group, { fontSize: "small" })
2355
- ), /* @__PURE__ */ React6.createElement(Box6, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React6.createElement(Typography6, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React6.createElement(
2356
- Typography6,
2772
+ /* @__PURE__ */ React7.createElement(Group, { fontSize: "small" })
2773
+ ), /* @__PURE__ */ React7.createElement(Box7, { flexGrow: 1, minWidth: 0 }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "subtitle1", sx: { fontWeight: 600, lineHeight: 1.3 } }, team.team_alias || "Untitled team"), /* @__PURE__ */ React7.createElement(
2774
+ Typography7,
2357
2775
  {
2358
2776
  variant: "caption",
2359
2777
  color: "text.secondary",
2360
2778
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2361
2779
  },
2362
2780
  team.team_id
2363
- )), isOver && /* @__PURE__ */ React6.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React6.createElement(StatusPill, { label: "Near limit", tone: "warning" }), /* @__PURE__ */ React6.createElement(
2364
- IconButton3,
2781
+ )), isOver && /* @__PURE__ */ React7.createElement(StatusPill, { label: "Over budget", tone: "danger" }), isNear && /* @__PURE__ */ React7.createElement(StatusPill, { label: "Near limit", tone: "warning" }), canManage && onEditTeam && /* @__PURE__ */ React7.createElement(
2782
+ Button5,
2783
+ {
2784
+ size: "small",
2785
+ variant: "text",
2786
+ onClick: () => onEditTeam(team),
2787
+ sx: { textTransform: "none" }
2788
+ },
2789
+ "Edit"
2790
+ ), /* @__PURE__ */ React7.createElement(
2791
+ IconButton4,
2365
2792
  {
2366
2793
  size: "small",
2367
2794
  onClick: () => setExpanded((e) => !e),
@@ -2373,9 +2800,9 @@ var init_TeamUsage = __esm({
2373
2800
  transition: theme.transitions.create("transform")
2374
2801
  })
2375
2802
  },
2376
- /* @__PURE__ */ React6.createElement(ExpandMore2, null)
2377
- )), /* @__PURE__ */ React6.createElement(
2378
- Box6,
2803
+ /* @__PURE__ */ React7.createElement(ExpandMore2, null)
2804
+ )), /* @__PURE__ */ React7.createElement(
2805
+ Box7,
2379
2806
  {
2380
2807
  sx: {
2381
2808
  display: "grid",
@@ -2385,35 +2812,35 @@ var init_TeamUsage = __esm({
2385
2812
  maxWidth: 640
2386
2813
  }
2387
2814
  },
2388
- /* @__PURE__ */ React6.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
2389
- /* @__PURE__ */ React6.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
2390
- /* @__PURE__ */ React6.createElement(Stat, { label: "Budget", value: budget > 0 ? fmtUsd2(budget) : "Unlimited" }),
2391
- /* @__PURE__ */ React6.createElement(Stat, { label: "Spend", value: fmtUsd2(spend) }),
2392
- /* @__PURE__ */ React6.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2393
- /* @__PURE__ */ React6.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2394
- ), budget > 0 && /* @__PURE__ */ React6.createElement(Box6, { mt: 2, maxWidth: 640 }, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", justifyContent: "space-between", alignItems: "baseline", mb: 0.75 }, /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, fmtUsd2(spend), " of ", fmtUsd2(budget)), /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary", sx: { fontVariantNumeric: "tabular-nums" } }, budgetPct.toFixed(0), "%")), /* @__PURE__ */ React6.createElement(Meter, { value: budgetPct, tone: budgetTone2(isOver, isNear), height: 5 })), /* @__PURE__ */ React6.createElement(Collapse, { in: expanded, unmountOnExit: true }, /* @__PURE__ */ React6.createElement(Divider2, { sx: { my: 2.5 } }), /* @__PURE__ */ React6.createElement(Box6, { mb: 2.5 }, sectionLabel("Models"), team.models?.length ? /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, team.models.map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m }))) : /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "All models allowed")), renderDailySpendSection(), /* @__PURE__ */ React6.createElement(Box6, null, sectionLabel("Members"), team.members_with_roles?.length ? /* @__PURE__ */ React6.createElement(
2815
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
2816
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
2817
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Budget", value: budget > 0 ? fmtUsd2(budget) : "Unlimited" }),
2818
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Spend", value: fmtUsd2(spend) }),
2819
+ /* @__PURE__ */ React7.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2820
+ /* @__PURE__ */ React7.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2821
+ ), 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(
2395
2822
  TableContainer3,
2396
2823
  {
2397
2824
  component: Paper4,
2398
2825
  variant: "outlined",
2399
2826
  sx: { borderRadius: 1.5 }
2400
2827
  },
2401
- /* @__PURE__ */ React6.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead3, null, /* @__PURE__ */ React6.createElement(TableRow3, null, /* @__PURE__ */ React6.createElement(TableCell3, null, "User"), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, "Role"))), /* @__PURE__ */ React6.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React6.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React6.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2" }, m.user_email), /* @__PURE__ */ React6.createElement(
2402
- Typography6,
2828
+ /* @__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(
2829
+ Typography7,
2403
2830
  {
2404
2831
  variant: "caption",
2405
2832
  color: "text.secondary",
2406
2833
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2407
2834
  },
2408
2835
  m.user_id
2409
- )) : /* @__PURE__ */ React6.createElement(
2410
- Typography6,
2836
+ )) : /* @__PURE__ */ React7.createElement(
2837
+ Typography7,
2411
2838
  {
2412
2839
  variant: "body2",
2413
2840
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2414
2841
  },
2415
2842
  m.user_id
2416
- )), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React6.createElement(
2843
+ )), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React7.createElement(
2417
2844
  StatusPill,
2418
2845
  {
2419
2846
  label: m.role,
@@ -2421,19 +2848,21 @@ var init_TeamUsage = __esm({
2421
2848
  dot: false
2422
2849
  }
2423
2850
  ))))))
2424
- ) : /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
2851
+ ) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
2425
2852
  };
2426
2853
  TeamUsage = ({
2427
2854
  teams,
2428
2855
  loading,
2429
2856
  getTeamUsage,
2430
- getTeamUsageLoading
2857
+ getTeamUsageLoading,
2858
+ canManage,
2859
+ onEditTeam
2431
2860
  }) => {
2432
2861
  if (loading) {
2433
- return /* @__PURE__ */ React6.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React6.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React6.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2862
+ 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 }));
2434
2863
  }
2435
2864
  if (!teams.length) {
2436
- return /* @__PURE__ */ React6.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React6.createElement(
2865
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(
2437
2866
  EmptyState,
2438
2867
  {
2439
2868
  message: "You're not a member of any LiteLLM team yet.",
@@ -2441,13 +2870,15 @@ var init_TeamUsage = __esm({
2441
2870
  }
2442
2871
  ));
2443
2872
  }
2444
- return /* @__PURE__ */ React6.createElement(SectionCard, { title: "Teams", subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}` }, /* @__PURE__ */ React6.createElement(Box6, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React6.createElement(
2873
+ 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(
2445
2874
  TeamCard,
2446
2875
  {
2447
2876
  key: team.team_id,
2448
2877
  team,
2449
2878
  usage: getTeamUsage(team.team_id),
2450
- usageLoading: getTeamUsageLoading(team.team_id)
2879
+ usageLoading: getTeamUsageLoading(team.team_id),
2880
+ canManage,
2881
+ onEditTeam
2451
2882
  }
2452
2883
  ))));
2453
2884
  };
@@ -2455,23 +2886,23 @@ var init_TeamUsage = __esm({
2455
2886
  });
2456
2887
 
2457
2888
  // src/components/ModelsTable.tsx
2458
- import React7, { useState as useState5, useMemo as useMemo5, useCallback } from "react";
2459
- import Box7 from "@mui/material/Box";
2889
+ import React8, { useState as useState6, useMemo as useMemo5, useCallback } from "react";
2890
+ import Box8 from "@mui/material/Box";
2460
2891
  import Table4 from "@mui/material/Table";
2461
2892
  import TableBody4 from "@mui/material/TableBody";
2462
2893
  import TableCell4 from "@mui/material/TableCell";
2463
2894
  import TableContainer4 from "@mui/material/TableContainer";
2464
2895
  import TableHead4 from "@mui/material/TableHead";
2465
2896
  import TableRow4 from "@mui/material/TableRow";
2466
- import Typography7 from "@mui/material/Typography";
2467
- import MenuItem3 from "@mui/material/MenuItem";
2897
+ import Typography8 from "@mui/material/Typography";
2898
+ import MenuItem4 from "@mui/material/MenuItem";
2468
2899
  import Select from "@mui/material/Select";
2469
2900
  import FormControl from "@mui/material/FormControl";
2470
2901
  import InputLabel from "@mui/material/InputLabel";
2471
2902
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
2472
2903
  import Tooltip3 from "@mui/material/Tooltip";
2473
2904
  import Snackbar from "@mui/material/Snackbar";
2474
- import Alert2 from "@mui/material/Alert";
2905
+ import Alert3 from "@mui/material/Alert";
2475
2906
  import { alpha as alpha5, useTheme as useTheme2 } from "@mui/material/styles";
2476
2907
  function isModelAllowedByTeam2(model, teamModels) {
2477
2908
  if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
@@ -2498,8 +2929,8 @@ var init_ModelsTable = __esm({
2498
2929
  ALL_PROXY_MODELS2 = "all-proxy-models";
2499
2930
  ModelsTable = ({ allModels, teams, loading }) => {
2500
2931
  const theme = useTheme2();
2501
- const [selectedTeamId, setSelectedTeamId] = useState5("");
2502
- const [copiedSnackbar, setCopiedSnackbar] = useState5(false);
2932
+ const [selectedTeamId, setSelectedTeamId] = useState6("");
2933
+ const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
2503
2934
  const selectedTeam = useMemo5(
2504
2935
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
2505
2936
  [teams, selectedTeamId]
@@ -2525,13 +2956,13 @@ var init_ModelsTable = __esm({
2525
2956
  return theme.palette.info.main;
2526
2957
  }
2527
2958
  };
2528
- return /* @__PURE__ */ React7.createElement(
2959
+ return /* @__PURE__ */ React8.createElement(
2529
2960
  SectionCard,
2530
2961
  {
2531
2962
  title: "Available Models",
2532
2963
  subtitle: selectedTeam ? `${filteredModels.length} model${filteredModels.length !== 1 ? "s" : ""} in ${selectedTeam.team_alias ?? selectedTeam.team_id}` : "Select a team to view its models"
2533
2964
  },
2534
- /* @__PURE__ */ React7.createElement(Box7, { sx: { px: 2.5, pb: 2.5, pt: 1 } }, /* @__PURE__ */ React7.createElement(FormControl, { size: "small", sx: { minWidth: 240, mb: 2 } }, /* @__PURE__ */ React7.createElement(InputLabel, { id: "team-select-label" }, "Team"), /* @__PURE__ */ React7.createElement(
2965
+ /* @__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(
2535
2966
  Select,
2536
2967
  {
2537
2968
  labelId: "team-select-label",
@@ -2539,25 +2970,25 @@ var init_ModelsTable = __esm({
2539
2970
  label: "Team",
2540
2971
  onChange: (e) => setSelectedTeamId(e.target.value)
2541
2972
  },
2542
- teams.map((t) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2973
+ teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem4, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2543
2974
  ))),
2544
- loading && /* @__PURE__ */ React7.createElement(EmptyState, { message: "Loading models\u2026" }),
2545
- !loading && !selectedTeamId && /* @__PURE__ */ React7.createElement(
2975
+ loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
2976
+ !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
2546
2977
  EmptyState,
2547
2978
  {
2548
2979
  message: "Select a team to see available models",
2549
2980
  hint: "Each team may have a different set of models assigned"
2550
2981
  }
2551
2982
  ),
2552
- !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React7.createElement(
2983
+ !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React8.createElement(
2553
2984
  EmptyState,
2554
2985
  {
2555
2986
  message: "No models available for this team",
2556
2987
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
2557
2988
  }
2558
2989
  ),
2559
- !loading && filteredModels.length > 0 && /* @__PURE__ */ React7.createElement(TableContainer4, null, /* @__PURE__ */ React7.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead4, null, /* @__PURE__ */ React7.createElement(TableRow4, null, /* @__PURE__ */ React7.createElement(TableCell4, null, "Model ID"), /* @__PURE__ */ React7.createElement(TableCell4, null, "Mode"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Input Cost"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Output Cost"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Max Input"), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, "Max Output"), /* @__PURE__ */ React7.createElement(TableCell4, null, "Capabilities"))), /* @__PURE__ */ React7.createElement(TableBody4, null, filteredModels.map((m) => /* @__PURE__ */ React7.createElement(TableRow4, { key: m.model_name, hover: true }, /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React7.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React7.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React7.createElement(
2560
- Box7,
2990
+ !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(
2991
+ Box8,
2561
2992
  {
2562
2993
  component: "span",
2563
2994
  role: "button",
@@ -2567,9 +2998,9 @@ var init_ModelsTable = __esm({
2567
2998
  sx: quietIconButtonSx("accent"),
2568
2999
  style: { cursor: "pointer", display: "inline-flex" }
2569
3000
  },
2570
- /* @__PURE__ */ React7.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
2571
- )))), /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(
2572
- Box7,
3001
+ /* @__PURE__ */ React8.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
3002
+ )))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(
3003
+ Box8,
2573
3004
  {
2574
3005
  component: "span",
2575
3006
  sx: {
@@ -2585,8 +3016,8 @@ var init_ModelsTable = __esm({
2585
3016
  }
2586
3017
  },
2587
3018
  m.mode
2588
- )), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React7.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React7.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React7.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
2589
- /* @__PURE__ */ React7.createElement(
3019
+ )), /* @__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" })))))))),
3020
+ /* @__PURE__ */ React8.createElement(
2590
3021
  Snackbar,
2591
3022
  {
2592
3023
  open: copiedSnackbar,
@@ -2594,7 +3025,7 @@ var init_ModelsTable = __esm({
2594
3025
  onClose: () => setCopiedSnackbar(false),
2595
3026
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
2596
3027
  },
2597
- /* @__PURE__ */ React7.createElement(Alert2, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3028
+ /* @__PURE__ */ React8.createElement(Alert3, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
2598
3029
  )
2599
3030
  );
2600
3031
  };
@@ -2602,9 +3033,9 @@ var init_ModelsTable = __esm({
2602
3033
  });
2603
3034
 
2604
3035
  // src/components/AuditLog.tsx
2605
- import React8, { useState as useState6, useCallback as useCallback2 } from "react";
2606
- import Box8 from "@mui/material/Box";
2607
- import Typography8 from "@mui/material/Typography";
3036
+ import React9, { useState as useState7, useCallback as useCallback2 } from "react";
3037
+ import Box9 from "@mui/material/Box";
3038
+ import Typography9 from "@mui/material/Typography";
2608
3039
  import Table5 from "@mui/material/Table";
2609
3040
  import TableBody5 from "@mui/material/TableBody";
2610
3041
  import TableCell5 from "@mui/material/TableCell";
@@ -2612,12 +3043,12 @@ import TableContainer5 from "@mui/material/TableContainer";
2612
3043
  import TableHead5 from "@mui/material/TableHead";
2613
3044
  import TableRow5 from "@mui/material/TableRow";
2614
3045
  import TablePagination from "@mui/material/TablePagination";
2615
- import TextField4 from "@mui/material/TextField";
2616
- import MenuItem4 from "@mui/material/MenuItem";
3046
+ import TextField5 from "@mui/material/TextField";
3047
+ import MenuItem5 from "@mui/material/MenuItem";
2617
3048
  import Skeleton5 from "@mui/material/Skeleton";
2618
3049
  import Collapse2 from "@mui/material/Collapse";
2619
- import IconButton4 from "@mui/material/IconButton";
2620
- import Alert3 from "@mui/material/Alert";
3050
+ import IconButton5 from "@mui/material/IconButton";
3051
+ import Alert4 from "@mui/material/Alert";
2621
3052
  import { alpha as alpha6 } from "@mui/material/styles";
2622
3053
  import { KeyboardArrowDown } from "@mui/icons-material";
2623
3054
  import { useAsync } from "react-use";
@@ -2641,10 +3072,10 @@ function formatDateTime(iso) {
2641
3072
  }
2642
3073
  function renderAuditLogBody(loading, entries) {
2643
3074
  if (loading) {
2644
- return /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React8.createElement(Skeleton5, { variant: "rounded", height: 160 })));
3075
+ return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
2645
3076
  }
2646
3077
  if (entries.length === 0) {
2647
- return /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React8.createElement(
3078
+ return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
2648
3079
  EmptyState,
2649
3080
  {
2650
3081
  message: "No audit events found",
@@ -2652,7 +3083,7 @@ function renderAuditLogBody(loading, entries) {
2652
3083
  }
2653
3084
  )));
2654
3085
  }
2655
- return entries.map((entry) => /* @__PURE__ */ React8.createElement(DetailRow, { key: entry.id, entry }));
3086
+ return entries.map((entry) => /* @__PURE__ */ React9.createElement(DetailRow, { key: entry.id, entry }));
2656
3087
  }
2657
3088
  var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
2658
3089
  var init_AuditLog = __esm({
@@ -2669,13 +3100,15 @@ var init_AuditLog = __esm({
2669
3100
  TABLE_LABELS = {
2670
3101
  LiteLLM_VerificationToken: "Key",
2671
3102
  LiteLLM_TeamTable: "Team",
3103
+ LiteLLM_TeamMembership: "Team member",
3104
+ LiteLLM_ObjectPermissionTable: "Team access (KB / MCP)",
2672
3105
  LiteLLM_UserTable: "User"
2673
3106
  };
2674
3107
  DetailRow = ({ entry }) => {
2675
- const [open, setOpen] = useState6(false);
3108
+ const [open, setOpen] = useState7(false);
2676
3109
  const hasDetail = entry.before_value || entry.updated_values;
2677
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React8.createElement(
2678
- IconButton4,
3110
+ 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(
3111
+ IconButton5,
2679
3112
  {
2680
3113
  size: "small",
2681
3114
  onClick: () => setOpen((o) => !o),
@@ -2687,9 +3120,9 @@ var init_AuditLog = __esm({
2687
3120
  transition: theme.transitions.create("transform")
2688
3121
  })
2689
3122
  },
2690
- /* @__PURE__ */ React8.createElement(KeyboardArrowDown, { fontSize: "small" })
2691
- )), /* @__PURE__ */ React8.createElement(TableCell5, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React8.createElement(TableCell5, null, entry.action && /* @__PURE__ */ React8.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React8.createElement(TableCell5, null, /* @__PURE__ */ React8.createElement(
2692
- Typography8,
3123
+ /* @__PURE__ */ React9.createElement(KeyboardArrowDown, { fontSize: "small" })
3124
+ )), /* @__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(
3125
+ Typography9,
2693
3126
  {
2694
3127
  variant: "body2",
2695
3128
  component: "code",
@@ -2698,8 +3131,8 @@ var init_AuditLog = __esm({
2698
3131
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
2699
3132
  },
2700
3133
  entry.object_id ? entry.object_id.slice(0, 20) + (entry.object_id.length > 20 ? "\u2026" : "") : "\u2014"
2701
- )), /* @__PURE__ */ React8.createElement(TableCell5, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React8.createElement(TableRow5, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React8.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React8.createElement(
2702
- Box8,
3134
+ )), /* @__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(
3135
+ Box9,
2703
3136
  {
2704
3137
  display: "flex",
2705
3138
  gap: 2,
@@ -2711,8 +3144,8 @@ var init_AuditLog = __esm({
2711
3144
  bgcolor: alpha6(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
2712
3145
  })
2713
3146
  },
2714
- entry.before_value && /* @__PURE__ */ React8.createElement(Box8, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React8.createElement(
2715
- Typography8,
3147
+ entry.before_value && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3148
+ Typography9,
2716
3149
  {
2717
3150
  variant: "caption",
2718
3151
  color: "text.secondary",
@@ -2721,9 +3154,9 @@ var init_AuditLog = __esm({
2721
3154
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2722
3155
  },
2723
3156
  "Before"
2724
- ), /* @__PURE__ */ React8.createElement(Typography8, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
2725
- entry.updated_values && /* @__PURE__ */ React8.createElement(Box8, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React8.createElement(
2726
- Typography8,
3157
+ ), /* @__PURE__ */ React9.createElement(Typography9, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.before_value, null, 2))),
3158
+ entry.updated_values && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3159
+ Typography9,
2727
3160
  {
2728
3161
  variant: "caption",
2729
3162
  color: "text.secondary",
@@ -2732,13 +3165,13 @@ var init_AuditLog = __esm({
2732
3165
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2733
3166
  },
2734
3167
  "After"
2735
- ), /* @__PURE__ */ React8.createElement(Typography8, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
3168
+ ), /* @__PURE__ */ React9.createElement(Typography9, { component: "pre", variant: "caption", sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", whiteSpace: "pre-wrap", wordBreak: "break-all", m: 0 } }, JSON.stringify(entry.updated_values, null, 2)))
2736
3169
  )))));
2737
3170
  };
2738
3171
  AuditLog = ({ api }) => {
2739
- const [page, setPage] = useState6(0);
2740
- const [pageSize, setPageSize] = useState6(25);
2741
- const [filters, setFilters] = useState6({});
3172
+ const [page, setPage] = useState7(0);
3173
+ const [pageSize, setPageSize] = useState7(25);
3174
+ const [filters, setFilters] = useState7({});
2742
3175
  const fetchParams = useCallback2(
2743
3176
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
2744
3177
  [page, pageSize, filters]
@@ -2749,14 +3182,14 @@ var init_AuditLog = __esm({
2749
3182
  );
2750
3183
  const entries = value?.audit_logs ?? [];
2751
3184
  const total = value?.total ?? 0;
2752
- return /* @__PURE__ */ React8.createElement(
3185
+ return /* @__PURE__ */ React9.createElement(
2753
3186
  SectionCard,
2754
3187
  {
2755
3188
  title: "Audit Log",
2756
3189
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
2757
3190
  flush: true,
2758
- actions: /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
2759
- TextField4,
3191
+ actions: /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
3192
+ TextField5,
2760
3193
  {
2761
3194
  size: "small",
2762
3195
  label: "Action",
@@ -2768,13 +3201,13 @@ var init_AuditLog = __esm({
2768
3201
  },
2769
3202
  sx: { minWidth: 150 }
2770
3203
  },
2771
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "" }, "All actions"),
2772
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "created" }, "Created"),
2773
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "updated" }, "Updated"),
2774
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "deleted" }, "Deleted"),
2775
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "blocked" }, "Blocked")
2776
- ), /* @__PURE__ */ React8.createElement(
2777
- TextField4,
3204
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All actions"),
3205
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "created" }, "Created"),
3206
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "updated" }, "Updated"),
3207
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "deleted" }, "Deleted"),
3208
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "blocked" }, "Blocked")
3209
+ ), /* @__PURE__ */ React9.createElement(
3210
+ TextField5,
2778
3211
  {
2779
3212
  size: "small",
2780
3213
  label: "Table",
@@ -2786,12 +3219,14 @@ var init_AuditLog = __esm({
2786
3219
  },
2787
3220
  sx: { minWidth: 150 }
2788
3221
  },
2789
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "" }, "All tables"),
2790
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_VerificationToken" }, "Key"),
2791
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_TeamTable" }, "Team"),
2792
- /* @__PURE__ */ React8.createElement(MenuItem4, { value: "LiteLLM_UserTable" }, "User")
2793
- ), /* @__PURE__ */ React8.createElement(
2794
- TextField4,
3222
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "" }, "All tables"),
3223
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_VerificationToken" }, "Key"),
3224
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamTable" }, "Team"),
3225
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_TeamMembership" }, "Team member"),
3226
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3227
+ /* @__PURE__ */ React9.createElement(MenuItem5, { value: "LiteLLM_UserTable" }, "User")
3228
+ ), /* @__PURE__ */ React9.createElement(
3229
+ TextField5,
2795
3230
  {
2796
3231
  size: "small",
2797
3232
  label: "Changed by",
@@ -2804,9 +3239,9 @@ var init_AuditLog = __esm({
2804
3239
  }
2805
3240
  ))
2806
3241
  },
2807
- error && /* @__PURE__ */ React8.createElement(Box8, { px: 2.5, pb: 2 }, /* @__PURE__ */ React8.createElement(Alert3, { severity: "error" }, error.message)),
2808
- /* @__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, { sx: { width: 40 } }), /* @__PURE__ */ React8.createElement(TableCell5, null, "Time"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Action"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Table"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Object ID"), /* @__PURE__ */ React8.createElement(TableCell5, null, "Changed By"))), /* @__PURE__ */ React8.createElement(TableBody5, null, renderAuditLogBody(loading, entries)))),
2809
- /* @__PURE__ */ React8.createElement(
3242
+ error && /* @__PURE__ */ React9.createElement(Box9, { px: 2.5, pb: 2 }, /* @__PURE__ */ React9.createElement(Alert4, { severity: "error" }, error.message)),
3243
+ /* @__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)))),
3244
+ /* @__PURE__ */ React9.createElement(
2810
3245
  TablePagination,
2811
3246
  {
2812
3247
  component: "div",
@@ -2827,22 +3262,53 @@ var init_AuditLog = __esm({
2827
3262
  }
2828
3263
  });
2829
3264
 
3265
+ // src/permissions.ts
3266
+ import { createPermission } from "@backstage/plugin-permission-common";
3267
+ var litellmTeamCreatePermission, litellmTeamManagePermission, litellmTeamMembersManagePermission, litellmTeamKnowledgebaseManagePermission, litellmTeamMcpManagePermission;
3268
+ var init_permissions = __esm({
3269
+ "src/permissions.ts"() {
3270
+ "use strict";
3271
+ litellmTeamCreatePermission = createPermission({
3272
+ name: "litellm.team.create",
3273
+ attributes: { action: "create" }
3274
+ });
3275
+ litellmTeamManagePermission = createPermission({
3276
+ name: "litellm.team.manage",
3277
+ attributes: { action: "update" }
3278
+ });
3279
+ litellmTeamMembersManagePermission = createPermission({
3280
+ name: "litellm.team.members.manage",
3281
+ attributes: { action: "update" }
3282
+ });
3283
+ litellmTeamKnowledgebaseManagePermission = createPermission({
3284
+ name: "litellm.team.knowledgebase.manage",
3285
+ attributes: { action: "update" }
3286
+ });
3287
+ litellmTeamMcpManagePermission = createPermission({
3288
+ name: "litellm.team.mcp.manage",
3289
+ attributes: { action: "update" }
3290
+ });
3291
+ }
3292
+ });
3293
+
2830
3294
  // src/components/LiteLLMPage.tsx
2831
3295
  var LiteLLMPage_exports = {};
2832
3296
  __export(LiteLLMPage_exports, {
2833
3297
  LiteLLMPage: () => LiteLLMPage
2834
3298
  });
2835
- import React9, { useState as useState7, useCallback as useCallback3, useMemo as useMemo6 } from "react";
2836
- import Box9 from "@mui/material/Box";
3299
+ import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo6 } from "react";
3300
+ import Box10 from "@mui/material/Box";
2837
3301
  import Snackbar2 from "@mui/material/Snackbar";
2838
- import Alert4 from "@mui/material/Alert";
3302
+ import Alert5 from "@mui/material/Alert";
2839
3303
  import CircularProgress4 from "@mui/material/CircularProgress";
2840
- import Typography9 from "@mui/material/Typography";
3304
+ import Typography10 from "@mui/material/Typography";
2841
3305
  import Paper5 from "@mui/material/Paper";
2842
3306
  import Tabs2 from "@mui/material/Tabs";
2843
3307
  import Tab2 from "@mui/material/Tab";
3308
+ import Button6 from "@mui/material/Button";
2844
3309
  import { useAsync as useAsync2, useAsyncRetry } from "react-use";
2845
3310
  import { useApi } from "@backstage/core-plugin-api";
3311
+ import { usePermission } from "@backstage/plugin-permission-react";
2846
3312
  function initDateRange() {
2847
3313
  let preset = "7d";
2848
3314
  try {
@@ -2864,27 +3330,30 @@ var init_LiteLLMPage = __esm({
2864
3330
  init_DashboardHeader();
2865
3331
  init_KeysTable();
2866
3332
  init_GenerateKeyDialog();
3333
+ init_ManageTeamDialog();
2867
3334
  init_UsageStats();
2868
3335
  init_TeamUsage();
2869
3336
  init_ModelsTable();
2870
3337
  init_AuditLog();
2871
3338
  init_api();
3339
+ init_permissions();
2872
3340
  PERIOD_LS_KEY2 = "litellm_usage_period";
2873
3341
  LiteLLMPage = () => {
2874
3342
  const api = useApi(liteLlmApiRef);
2875
- const [dateRange, setDateRange] = useState7(initDateRange);
2876
- const [currentPreset, setCurrentPreset] = useState7(() => {
3343
+ const [dateRange, setDateRange] = useState8(initDateRange);
3344
+ const [currentPreset, setCurrentPreset] = useState8(() => {
2877
3345
  try {
2878
3346
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
2879
3347
  } catch {
2880
3348
  return "7d";
2881
3349
  }
2882
3350
  });
2883
- const [activeTab, setActiveTab] = useState7("overview");
2884
- const [snackbar, setSnackbar] = useState7(null);
2885
- const [generateDialogOpen, setGenerateDialogOpen] = useState7(false);
2886
- const [teamUsageCache, setTeamUsageCache] = useState7({});
2887
- const [teamUsageLoading, setTeamUsageLoading] = useState7({});
3351
+ const [activeTab, setActiveTab] = useState8("overview");
3352
+ const [snackbar, setSnackbar] = useState8(null);
3353
+ const [generateDialogOpen, setGenerateDialogOpen] = useState8(false);
3354
+ const [manageTeam, setManageTeam] = useState8(null);
3355
+ const [teamUsageCache, setTeamUsageCache] = useState8({});
3356
+ const [teamUsageLoading, setTeamUsageLoading] = useState8({});
2888
3357
  const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
2889
3358
  () => api.getUserInfo(),
2890
3359
  [api]
@@ -2904,11 +3373,30 @@ var init_LiteLLMPage = __esm({
2904
3373
  () => api.listModels().catch(() => []),
2905
3374
  [api]
2906
3375
  );
2907
- const { value: allTeams, loading: teamsLoading } = useAsync2(
3376
+ const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = useAsyncRetry(
2908
3377
  () => api.getTeams().catch(() => []),
2909
3378
  [api]
2910
3379
  );
2911
3380
  const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
3381
+ const { value: managedTeams } = useAsync2(
3382
+ async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
3383
+ [api, liteLlmConfig]
3384
+ );
3385
+ const { allowed: canCreateTeam } = usePermission({ permission: litellmTeamCreatePermission });
3386
+ const { allowed: canManageTeam } = usePermission({ permission: litellmTeamManagePermission });
3387
+ const { allowed: canManageMembers } = usePermission({ permission: litellmTeamMembersManagePermission });
3388
+ const { allowed: canManageKnowledgeBases } = usePermission({ permission: litellmTeamKnowledgebaseManagePermission });
3389
+ const { allowed: canManageMcpServers } = usePermission({ permission: litellmTeamMcpManagePermission });
3390
+ const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
3391
+ const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
3392
+ const { value: vectorStores } = useAsync2(
3393
+ async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
3394
+ [api, objectPermsEnabled]
3395
+ );
3396
+ const { value: mcpServers } = useAsync2(
3397
+ async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3398
+ [api, objectPermsEnabled]
3399
+ );
2912
3400
  const teams = useMemo6(() => {
2913
3401
  if (!allTeams?.length) return [];
2914
3402
  if (!userInfo) return allTeams;
@@ -3056,14 +3544,14 @@ var init_LiteLLMPage = __esm({
3056
3544
  );
3057
3545
  const isInitialLoading = userLoading && !userInfo;
3058
3546
  if (isInitialLoading) {
3059
- return /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React9.createElement(CircularProgress4, null));
3547
+ return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress4, null));
3060
3548
  }
3061
3549
  if (userError || !userInfo) {
3062
3550
  const isProvisioningEnabled = userError?.body?.provisioning === true;
3063
3551
  const hint = userError?.body?.hint;
3064
- return /* @__PURE__ */ React9.createElement(Box9, { p: 3 }, /* @__PURE__ */ React9.createElement(Paper5, { sx: { p: 3 } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React9.createElement(Typography9, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React9.createElement(Typography9, { 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.")));
3552
+ 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.")));
3065
3553
  }
3066
- const pageTabs = /* @__PURE__ */ React9.createElement(
3554
+ const pageTabs = /* @__PURE__ */ React10.createElement(
3067
3555
  Tabs2,
3068
3556
  {
3069
3557
  value: activeTab,
@@ -3076,13 +3564,13 @@ var init_LiteLLMPage = __esm({
3076
3564
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
3077
3565
  }
3078
3566
  },
3079
- /* @__PURE__ */ React9.createElement(Tab2, { label: "Overview", value: "overview" }),
3080
- /* @__PURE__ */ React9.createElement(Tab2, { label: "Keys", value: "keys" }),
3081
- /* @__PURE__ */ React9.createElement(Tab2, { label: "Teams", value: "teams" }),
3082
- /* @__PURE__ */ React9.createElement(Tab2, { label: "Models", value: "models" }),
3083
- userInfo.can_view_audit && /* @__PURE__ */ React9.createElement(Tab2, { label: "Audit Log", value: "audit" })
3567
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Overview", value: "overview" }),
3568
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Keys", value: "keys" }),
3569
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Teams", value: "teams" }),
3570
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Models", value: "models" }),
3571
+ userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(Tab2, { label: "Audit Log", value: "audit" })
3084
3572
  );
3085
- return /* @__PURE__ */ React9.createElement(Box9, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React9.createElement(
3573
+ return /* @__PURE__ */ React10.createElement(Box10, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React10.createElement(
3086
3574
  DashboardHeader,
3087
3575
  {
3088
3576
  userInfo,
@@ -3092,7 +3580,7 @@ var init_LiteLLMPage = __esm({
3092
3580
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
3093
3581
  tabs: pageTabs
3094
3582
  }
3095
- ), activeTab === "overview" && /* @__PURE__ */ React9.createElement(
3583
+ ), activeTab === "overview" && /* @__PURE__ */ React10.createElement(
3096
3584
  UsageStats,
3097
3585
  {
3098
3586
  usage: usage ?? null,
@@ -3103,7 +3591,7 @@ var init_LiteLLMPage = __esm({
3103
3591
  loading: usageLoading,
3104
3592
  userInfo
3105
3593
  }
3106
- ), activeTab === "keys" && /* @__PURE__ */ React9.createElement(
3594
+ ), activeTab === "keys" && /* @__PURE__ */ React10.createElement(
3107
3595
  KeysTable,
3108
3596
  {
3109
3597
  keys: keys ?? [],
@@ -3117,25 +3605,40 @@ var init_LiteLLMPage = __esm({
3117
3605
  onDeleteKey: handleDeleteKey,
3118
3606
  onPruneExpiredKeys: handlePruneExpiredKeys
3119
3607
  }
3120
- ), activeTab === "teams" && /* @__PURE__ */ React9.createElement(
3121
- TeamUsage,
3122
- {
3123
- teams: teams ?? [],
3124
- loading: teamsLoading,
3125
- getTeamUsage: (teamId) => {
3126
- if (teamUsageCache[teamId] === void 0) loadTeamUsage(teamId);
3127
- return teamUsageCache[teamId] ?? null;
3608
+ ), activeTab === "teams" && (() => {
3609
+ const teamsById = /* @__PURE__ */ new Map();
3610
+ for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
3611
+ for (const t of teams ?? []) teamsById.set(t.team_id, t);
3612
+ const visibleTeams = Array.from(teamsById.values());
3613
+ 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(
3614
+ Button6,
3615
+ {
3616
+ variant: "contained",
3617
+ onClick: () => setManageTeam({ mode: "create" })
3128
3618
  },
3129
- getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false
3130
- }
3131
- ), activeTab === "models" && /* @__PURE__ */ React9.createElement(
3619
+ "Create Team"
3620
+ )), /* @__PURE__ */ React10.createElement(
3621
+ TeamUsage,
3622
+ {
3623
+ teams: visibleTeams,
3624
+ loading: teamsLoading,
3625
+ getTeamUsage: (teamId) => {
3626
+ if (teamUsageCache[teamId] === void 0) loadTeamUsage(teamId);
3627
+ return teamUsageCache[teamId] ?? null;
3628
+ },
3629
+ getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
3630
+ canManage: teamMgmtEnabled && canManageTeam,
3631
+ onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
3632
+ }
3633
+ ));
3634
+ })(), activeTab === "models" && /* @__PURE__ */ React10.createElement(
3132
3635
  ModelsTable,
3133
3636
  {
3134
3637
  allModels: allModels ?? [],
3135
3638
  teams: teams ?? [],
3136
3639
  loading: modelsLoading
3137
3640
  }
3138
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React9.createElement(AuditLog, { api }), /* @__PURE__ */ React9.createElement(
3641
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(AuditLog, { api }), /* @__PURE__ */ React10.createElement(
3139
3642
  GenerateKeyDialog,
3140
3643
  {
3141
3644
  open: generateDialogOpen,
@@ -3148,7 +3651,69 @@ var init_LiteLLMPage = __esm({
3148
3651
  onGenerateKey: handleGenerateKey,
3149
3652
  onGetConfig: () => api.getConfig()
3150
3653
  }
3151
- ), /* @__PURE__ */ React9.createElement(
3654
+ ), /* @__PURE__ */ React10.createElement(
3655
+ ManageTeamDialog,
3656
+ {
3657
+ open: !!manageTeam,
3658
+ onClose: () => setManageTeam(null),
3659
+ mode: manageTeam?.mode ?? "create",
3660
+ team: manageTeam?.team,
3661
+ allModels: allModels ?? [],
3662
+ config: liteLlmConfig,
3663
+ onSubmit: async (payload) => {
3664
+ if (manageTeam?.mode === "edit" && manageTeam.team) {
3665
+ await api.updateTeam(manageTeam.team.team_id, payload);
3666
+ } else {
3667
+ await api.createTeam(payload);
3668
+ }
3669
+ setSnackbar({ message: "Team saved", severity: "success" });
3670
+ refreshTeams();
3671
+ },
3672
+ canManageMembers: teamMgmtEnabled && canManageMembers,
3673
+ onAddMember: async (userEntityRef, maxBudgetInTeam) => {
3674
+ if (!manageTeam?.team) return;
3675
+ const updated = await api.addTeamMember(manageTeam.team.team_id, {
3676
+ userEntityRef,
3677
+ ...maxBudgetInTeam ? { maxBudgetInTeam } : {}
3678
+ });
3679
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3680
+ refreshTeams();
3681
+ },
3682
+ onRemoveMember: async (userEntityRef) => {
3683
+ if (!manageTeam?.team) return;
3684
+ const updated = await api.removeTeamMember(
3685
+ manageTeam.team.team_id,
3686
+ userEntityRef
3687
+ );
3688
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3689
+ refreshTeams();
3690
+ },
3691
+ canManageKnowledgeBases: teamMgmtEnabled && objectPermsEnabled && canManageKnowledgeBases,
3692
+ vectorStores: vectorStores ?? [],
3693
+ onSaveKnowledgeBases: async (vectorStoreIds) => {
3694
+ if (!manageTeam?.team) return;
3695
+ const updated = await api.setTeamKnowledgeBases(
3696
+ manageTeam.team.team_id,
3697
+ vectorStoreIds
3698
+ );
3699
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3700
+ setSnackbar({ message: "Knowledge bases updated", severity: "success" });
3701
+ refreshTeams();
3702
+ },
3703
+ canManageMcpServers: teamMgmtEnabled && objectPermsEnabled && canManageMcpServers,
3704
+ mcpServers: mcpServers ?? [],
3705
+ onSaveMcpServers: async (mcpServerIds) => {
3706
+ if (!manageTeam?.team) return;
3707
+ const updated = await api.setTeamMcpServers(
3708
+ manageTeam.team.team_id,
3709
+ mcpServerIds
3710
+ );
3711
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3712
+ setSnackbar({ message: "MCP servers updated", severity: "success" });
3713
+ refreshTeams();
3714
+ }
3715
+ }
3716
+ ), /* @__PURE__ */ React10.createElement(
3152
3717
  Snackbar2,
3153
3718
  {
3154
3719
  open: !!snackbar,
@@ -3156,7 +3721,7 @@ var init_LiteLLMPage = __esm({
3156
3721
  onClose: () => setSnackbar(null),
3157
3722
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
3158
3723
  },
3159
- snackbar ? /* @__PURE__ */ React9.createElement(Alert4, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3724
+ snackbar ? /* @__PURE__ */ React10.createElement(Alert5, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3160
3725
  ));
3161
3726
  };
3162
3727
  }
@@ -3164,7 +3729,7 @@ var init_LiteLLMPage = __esm({
3164
3729
 
3165
3730
  // src/plugin.tsx
3166
3731
  init_api();
3167
- import React10 from "react";
3732
+ import React11 from "react";
3168
3733
  import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
3169
3734
  import {
3170
3735
  createFrontendPlugin,
@@ -3183,10 +3748,10 @@ var liteLlmPage = PageBlueprint.make({
3183
3748
  params: {
3184
3749
  path: "/litellm",
3185
3750
  title: "LiteLLM",
3186
- icon: /* @__PURE__ */ React10.createElement(TrendingUpIcon, null),
3751
+ icon: /* @__PURE__ */ React11.createElement(TrendingUpIcon, null),
3187
3752
  loader: async () => {
3188
3753
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
3189
- return /* @__PURE__ */ React10.createElement(LiteLLMPage2, null);
3754
+ return /* @__PURE__ */ React11.createElement(LiteLLMPage2, null);
3190
3755
  }
3191
3756
  }
3192
3757
  });
@@ -3205,16 +3770,16 @@ init_TeamUsage();
3205
3770
  // src/components/LiteLLMHomeWidget.tsx
3206
3771
  init_api();
3207
3772
  init_format();
3208
- import React11, { useState as useState8, useEffect as useEffect2 } from "react";
3773
+ import React12, { useState as useState9, useEffect as useEffect3 } from "react";
3209
3774
  import Paper6 from "@mui/material/Paper";
3210
- import Box10 from "@mui/material/Box";
3211
- import Typography10 from "@mui/material/Typography";
3775
+ import Box11 from "@mui/material/Box";
3776
+ import Typography11 from "@mui/material/Typography";
3212
3777
  import FormControl2 from "@mui/material/FormControl";
3213
3778
  import Select2 from "@mui/material/Select";
3214
- import MenuItem5 from "@mui/material/MenuItem";
3779
+ import MenuItem6 from "@mui/material/MenuItem";
3215
3780
  import Grid2 from "@mui/material/Grid";
3216
3781
  import CircularProgress5 from "@mui/material/CircularProgress";
3217
- import Alert5 from "@mui/material/Alert";
3782
+ import Alert6 from "@mui/material/Alert";
3218
3783
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
3219
3784
  import { useApi as useApi2 } from "@backstage/core-plugin-api";
3220
3785
  function presetToDateRange(preset) {
@@ -3229,19 +3794,19 @@ function presetToDateRange(preset) {
3229
3794
  }
3230
3795
  return { start, end };
3231
3796
  }
3232
- var Kpi = ({ label, value }) => /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Typography10, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React11.createElement(Typography10, { variant: "subtitle1", fontWeight: 600 }, value));
3797
+ var Kpi = ({ label, value }) => /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Typography11, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React12.createElement(Typography11, { variant: "subtitle1", fontWeight: 600 }, value));
3233
3798
  var LiteLLMHomeWidget = ({
3234
3799
  defaultPeriod = "7d",
3235
3800
  title = "LiteLLM Usage"
3236
3801
  }) => {
3237
3802
  const api = useApi2(liteLlmApiRef);
3238
- const [period, setPeriod] = useState8(defaultPeriod);
3239
- const [loading, setLoading] = useState8(true);
3240
- const [usageError, setUsageError] = useState8(null);
3241
- const [keysError, setKeysError] = useState8(null);
3242
- const [usage, setUsage] = useState8(null);
3243
- const [keys, setKeys] = useState8([]);
3244
- useEffect2(() => {
3803
+ const [period, setPeriod] = useState9(defaultPeriod);
3804
+ const [loading, setLoading] = useState9(true);
3805
+ const [usageError, setUsageError] = useState9(null);
3806
+ const [keysError, setKeysError] = useState9(null);
3807
+ const [usage, setUsage] = useState9(null);
3808
+ const [keys, setKeys] = useState9([]);
3809
+ useEffect3(() => {
3245
3810
  let cancelled = false;
3246
3811
  setLoading(true);
3247
3812
  setUsageError(null);
@@ -3278,17 +3843,17 @@ var LiteLLMHomeWidget = ({
3278
3843
  spend: d.spend
3279
3844
  }));
3280
3845
  const hasSparkline = dailyData.length > 0;
3281
- return /* @__PURE__ */ React11.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React11.createElement(Box10, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React11.createElement(Typography10, { variant: "h6" }, title), /* @__PURE__ */ React11.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React11.createElement(
3846
+ 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(
3282
3847
  Select2,
3283
3848
  {
3284
3849
  value: period,
3285
3850
  onChange: (e) => setPeriod(e.target.value),
3286
3851
  displayEmpty: true
3287
3852
  },
3288
- /* @__PURE__ */ React11.createElement(MenuItem5, { value: "today" }, "Today"),
3289
- /* @__PURE__ */ React11.createElement(MenuItem5, { value: "7d" }, "7d"),
3290
- /* @__PURE__ */ React11.createElement(MenuItem5, { value: "30d" }, "30d")
3291
- ))), loading && /* @__PURE__ */ React11.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React11.createElement(CircularProgress5, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React11.createElement(Alert5, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React11.createElement(React11.Fragment, null, partialFailure && /* @__PURE__ */ React11.createElement(Alert5, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React11.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React11.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React11.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React11.createElement(Box10, { height: 120 }, /* @__PURE__ */ React11.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React11.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React11.createElement(
3853
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "today" }, "Today"),
3854
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "7d" }, "7d"),
3855
+ /* @__PURE__ */ React12.createElement(MenuItem6, { value: "30d" }, "30d")
3856
+ ))), 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(
3292
3857
  Area3,
3293
3858
  {
3294
3859
  type: "monotone",
@@ -3304,7 +3869,9 @@ var LiteLLMHomeWidget = ({
3304
3869
 
3305
3870
  // src/index.ts
3306
3871
  init_GenerateKeyDialog();
3872
+ init_ManageTeamDialog();
3307
3873
  init_api();
3874
+ init_permissions();
3308
3875
  export {
3309
3876
  DashboardHeader,
3310
3877
  GenerateKeyDialog,
@@ -3312,9 +3879,15 @@ export {
3312
3879
  LiteLLMHomeWidget,
3313
3880
  LiteLLMPage,
3314
3881
  LiteLlmApi,
3882
+ ManageTeamDialog,
3315
3883
  TeamUsage,
3316
3884
  UsageStats,
3317
3885
  liteLlmApiRef,
3318
- litellmPlugin
3886
+ litellmPlugin,
3887
+ litellmTeamCreatePermission,
3888
+ litellmTeamKnowledgebaseManagePermission,
3889
+ litellmTeamManagePermission,
3890
+ litellmTeamMcpManagePermission,
3891
+ litellmTeamMembersManagePermission
3319
3892
  };
3320
3893
  //# sourceMappingURL=index.esm.js.map