@acarmisc/backstage-plugin-litellm 0.15.4 → 0.16.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,12 +1919,351 @@ 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";
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";
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";
1863
1934
  import Box5 from "@mui/material/Box";
1935
+ import Divider2 from "@mui/material/Divider";
1864
1936
  import Typography5 from "@mui/material/Typography";
1865
- import TextField3 from "@mui/material/TextField";
1937
+ import IconButton3 from "@mui/material/IconButton";
1938
+ import List from "@mui/material/List";
1939
+ import ListItem from "@mui/material/ListItem";
1940
+ import ListItemText from "@mui/material/ListItemText";
1941
+ import { Delete as DeleteIcon } from "@mui/icons-material";
1942
+ var ManageTeamDialog;
1943
+ var init_ManageTeamDialog = __esm({
1944
+ "src/components/ManageTeamDialog.tsx"() {
1945
+ "use strict";
1946
+ ManageTeamDialog = ({
1947
+ open,
1948
+ onClose,
1949
+ mode,
1950
+ team,
1951
+ allModels,
1952
+ config,
1953
+ onSubmit,
1954
+ canManageMembers,
1955
+ onAddMember,
1956
+ onRemoveMember,
1957
+ canManageKnowledgeBases,
1958
+ vectorStores,
1959
+ onSaveKnowledgeBases,
1960
+ canManageMcpServers,
1961
+ mcpServers,
1962
+ onSaveMcpServers
1963
+ }) => {
1964
+ const [alias, setAlias] = useState3("");
1965
+ const [models, setModels] = useState3([]);
1966
+ const [maxBudget, setMaxBudget] = useState3("");
1967
+ const [unlimited, setUnlimited] = useState3(false);
1968
+ const [budgetDuration, setBudgetDuration] = useState3("");
1969
+ const [submitting, setSubmitting] = useState3(false);
1970
+ const [error, setError] = useState3(null);
1971
+ const [memberRef, setMemberRef] = useState3("");
1972
+ const [memberBudget, setMemberBudget] = useState3("");
1973
+ const [memberBusy, setMemberBusy] = useState3(false);
1974
+ const [memberError, setMemberError] = useState3(null);
1975
+ const [kbIds, setKbIds] = useState3([]);
1976
+ const [kbBusy, setKbBusy] = useState3(false);
1977
+ const [kbError, setKbError] = useState3(null);
1978
+ const [mcpIds, setMcpIds] = useState3([]);
1979
+ const [mcpBusy, setMcpBusy] = useState3(false);
1980
+ const [mcpError, setMcpError] = useState3(null);
1981
+ const allowUnlimitedBudget = config?.teamManagement?.allowUnlimitedBudget ?? false;
1982
+ const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling;
1983
+ useEffect2(() => {
1984
+ if (open) {
1985
+ if (mode === "edit" && team) {
1986
+ setAlias(team.team_alias ?? "");
1987
+ setModels(team.models ?? []);
1988
+ setMaxBudget(team.max_budget ? String(team.max_budget) : "");
1989
+ setUnlimited(allowUnlimitedBudget && !team.max_budget);
1990
+ setBudgetDuration("");
1991
+ } else {
1992
+ setAlias("");
1993
+ setModels([]);
1994
+ setMaxBudget("");
1995
+ setUnlimited(allowUnlimitedBudget);
1996
+ setBudgetDuration("");
1997
+ }
1998
+ setError(null);
1999
+ setMemberRef("");
2000
+ setMemberBudget("");
2001
+ setMemberError(null);
2002
+ setKbIds(team?.object_permission?.vector_stores ?? []);
2003
+ setKbError(null);
2004
+ setMcpIds(team?.object_permission?.mcp_servers ?? []);
2005
+ setMcpError(null);
2006
+ }
2007
+ }, [open, mode, team, allowUnlimitedBudget]);
2008
+ const handleSubmit = async () => {
2009
+ setError(null);
2010
+ if (!alias.trim()) {
2011
+ setError("Team alias is required");
2012
+ return;
2013
+ }
2014
+ if (models.length === 0) {
2015
+ setError("At least one model is required");
2016
+ return;
2017
+ }
2018
+ if (!unlimited) {
2019
+ if (!maxBudget) {
2020
+ setError("Budget is required when unlimited budgets are disabled");
2021
+ return;
2022
+ }
2023
+ const budget = parseFloat(maxBudget);
2024
+ if (isNaN(budget) || budget <= 0) {
2025
+ setError("Budget must be a positive number");
2026
+ return;
2027
+ }
2028
+ if (maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 && budget > maxBudgetCeiling) {
2029
+ setError(`Budget cannot exceed $${maxBudgetCeiling}`);
2030
+ return;
2031
+ }
2032
+ }
2033
+ try {
2034
+ setSubmitting(true);
2035
+ let payload;
2036
+ if (mode === "create") {
2037
+ payload = {
2038
+ team_alias: alias.trim(),
2039
+ models,
2040
+ ...unlimited ? { max_budget: null } : { max_budget: parseFloat(maxBudget) },
2041
+ ...budgetDuration && { budget_duration: budgetDuration }
2042
+ };
2043
+ } else {
2044
+ payload = {
2045
+ team_alias: alias.trim(),
2046
+ models,
2047
+ ...unlimited ? { max_budget: null } : { max_budget: parseFloat(maxBudget) },
2048
+ ...budgetDuration && { budget_duration: budgetDuration }
2049
+ };
2050
+ }
2051
+ await onSubmit(payload);
2052
+ onClose();
2053
+ } catch (err) {
2054
+ setError(err instanceof Error ? err.message : "An error occurred");
2055
+ } finally {
2056
+ setSubmitting(false);
2057
+ }
2058
+ };
2059
+ const handleAddMember = async () => {
2060
+ if (!onAddMember || !memberRef.trim()) return;
2061
+ setMemberError(null);
2062
+ const budget = memberBudget ? parseFloat(memberBudget) : void 0;
2063
+ if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
2064
+ setMemberError("Max budget in team must be a positive number");
2065
+ return;
2066
+ }
2067
+ try {
2068
+ setMemberBusy(true);
2069
+ await onAddMember(memberRef.trim(), budget);
2070
+ setMemberRef("");
2071
+ setMemberBudget("");
2072
+ } catch (err) {
2073
+ setMemberError(err instanceof Error ? err.message : "Failed to add member");
2074
+ } finally {
2075
+ setMemberBusy(false);
2076
+ }
2077
+ };
2078
+ const handleRemoveMember = async (userId) => {
2079
+ if (!onRemoveMember) return;
2080
+ setMemberError(null);
2081
+ try {
2082
+ setMemberBusy(true);
2083
+ await onRemoveMember(userId);
2084
+ } catch (err) {
2085
+ setMemberError(err instanceof Error ? err.message : "Failed to remove member");
2086
+ } finally {
2087
+ setMemberBusy(false);
2088
+ }
2089
+ };
2090
+ const handleSaveKnowledgeBases = async () => {
2091
+ if (!onSaveKnowledgeBases) return;
2092
+ setKbError(null);
2093
+ try {
2094
+ setKbBusy(true);
2095
+ await onSaveKnowledgeBases(kbIds);
2096
+ } catch (err) {
2097
+ setKbError(err instanceof Error ? err.message : "Failed to save knowledge bases");
2098
+ } finally {
2099
+ setKbBusy(false);
2100
+ }
2101
+ };
2102
+ const handleSaveMcpServers = async () => {
2103
+ if (!onSaveMcpServers) return;
2104
+ setMcpError(null);
2105
+ try {
2106
+ setMcpBusy(true);
2107
+ await onSaveMcpServers(mcpIds);
2108
+ } catch (err) {
2109
+ setMcpError(err instanceof Error ? err.message : "Failed to save MCP servers");
2110
+ } finally {
2111
+ setMcpBusy(false);
2112
+ }
2113
+ };
2114
+ const modelNames = allModels.map((m) => m.model_name);
2115
+ const members = team?.members_with_roles ?? [];
2116
+ const showMembers = mode === "edit" && !!canManageMembers;
2117
+ const showKnowledgeBases = mode === "edit" && !!canManageKnowledgeBases;
2118
+ const kbOptions = (vectorStores ?? []).map((v) => v.id);
2119
+ const kbLabel = (id) => (vectorStores ?? []).find((v) => v.id === id)?.name ?? id;
2120
+ const showMcpServers = mode === "edit" && !!canManageMcpServers;
2121
+ const mcpOptions = (mcpServers ?? []).map((s) => s.id);
2122
+ const mcpLabel = (id) => (mcpServers ?? []).find((s) => s.id === id)?.name ?? id;
2123
+ 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(
2124
+ TextField3,
2125
+ {
2126
+ label: "Team Alias",
2127
+ value: alias,
2128
+ onChange: (e) => setAlias(e.target.value),
2129
+ disabled: submitting,
2130
+ fullWidth: true
2131
+ }
2132
+ ), /* @__PURE__ */ React5.createElement(
2133
+ Autocomplete3,
2134
+ {
2135
+ multiple: true,
2136
+ options: modelNames,
2137
+ value: models,
2138
+ onChange: (_, newValue) => setModels(newValue),
2139
+ disabled: submitting,
2140
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Models" })
2141
+ }
2142
+ ), /* @__PURE__ */ React5.createElement(
2143
+ TextField3,
2144
+ {
2145
+ label: "Max Budget ($)",
2146
+ type: "number",
2147
+ value: maxBudget,
2148
+ onChange: (e) => setMaxBudget(e.target.value),
2149
+ disabled: submitting || unlimited,
2150
+ helperText: maxBudgetCeiling !== null && maxBudgetCeiling !== void 0 ? `Maximum: $${maxBudgetCeiling}` : void 0,
2151
+ fullWidth: true
2152
+ }
2153
+ ), allowUnlimitedBudget && /* @__PURE__ */ React5.createElement(
2154
+ FormControlLabel2,
2155
+ {
2156
+ control: /* @__PURE__ */ React5.createElement(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
2157
+ label: "Unlimited Budget"
2158
+ }
2159
+ ), /* @__PURE__ */ React5.createElement(
2160
+ TextField3,
2161
+ {
2162
+ label: "Budget Duration",
2163
+ value: budgetDuration,
2164
+ onChange: (e) => setBudgetDuration(e.target.value),
2165
+ placeholder: "30d",
2166
+ disabled: submitting,
2167
+ fullWidth: true
2168
+ }
2169
+ ), showMembers && /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Divider2, { sx: { my: 1 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", sx: { mb: 1 } }, "Members"), memberError && /* @__PURE__ */ React5.createElement(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null) }, memberError), members.length === 0 ? /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary" }, "No members yet.") : /* @__PURE__ */ React5.createElement(List, { dense: true, disablePadding: true }, members.map((m) => /* @__PURE__ */ React5.createElement(
2170
+ ListItem,
2171
+ {
2172
+ key: m.user_id,
2173
+ disableGutters: true,
2174
+ secondaryAction: /* @__PURE__ */ React5.createElement(
2175
+ IconButton3,
2176
+ {
2177
+ edge: "end",
2178
+ size: "small",
2179
+ "aria-label": `remove ${m.user_id}`,
2180
+ disabled: memberBusy,
2181
+ onClick: () => handleRemoveMember(m.user_id)
2182
+ },
2183
+ /* @__PURE__ */ React5.createElement(DeleteIcon, { fontSize: "small" })
2184
+ )
2185
+ },
2186
+ /* @__PURE__ */ React5.createElement(ListItemText, { primary: m.user_id, secondary: m.role })
2187
+ ))), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", gap: 1, alignItems: "flex-start", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2188
+ TextField3,
2189
+ {
2190
+ label: "Backstage user ref",
2191
+ placeholder: "user:default/alice",
2192
+ value: memberRef,
2193
+ onChange: (e) => setMemberRef(e.target.value),
2194
+ disabled: memberBusy,
2195
+ size: "small",
2196
+ sx: { flex: 1 }
2197
+ }
2198
+ ), /* @__PURE__ */ React5.createElement(
2199
+ TextField3,
2200
+ {
2201
+ label: "Max budget in team ($)",
2202
+ type: "number",
2203
+ value: memberBudget,
2204
+ onChange: (e) => setMemberBudget(e.target.value),
2205
+ disabled: memberBusy,
2206
+ size: "small",
2207
+ sx: { width: 160 }
2208
+ }
2209
+ ), /* @__PURE__ */ React5.createElement(
2210
+ Button4,
2211
+ {
2212
+ onClick: handleAddMember,
2213
+ disabled: memberBusy || !memberRef.trim(),
2214
+ variant: "outlined",
2215
+ sx: { mt: 0.5 }
2216
+ },
2217
+ "Add"
2218
+ ))), 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(
2219
+ Autocomplete3,
2220
+ {
2221
+ multiple: true,
2222
+ options: kbOptions,
2223
+ value: kbIds,
2224
+ getOptionLabel: kbLabel,
2225
+ onChange: (_, v) => setKbIds(v),
2226
+ disabled: kbBusy,
2227
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Attached knowledge bases" })
2228
+ }
2229
+ ), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2230
+ Button4,
2231
+ {
2232
+ onClick: handleSaveKnowledgeBases,
2233
+ disabled: kbBusy,
2234
+ variant: "outlined"
2235
+ },
2236
+ kbBusy ? "Saving\u2026" : "Save knowledge bases"
2237
+ ))), 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(
2238
+ Autocomplete3,
2239
+ {
2240
+ multiple: true,
2241
+ options: mcpOptions,
2242
+ value: mcpIds,
2243
+ getOptionLabel: mcpLabel,
2244
+ onChange: (_, v) => setMcpIds(v),
2245
+ disabled: mcpBusy,
2246
+ renderInput: (params) => /* @__PURE__ */ React5.createElement(TextField3, { ...params, label: "Attached MCP servers" })
2247
+ }
2248
+ ), /* @__PURE__ */ React5.createElement(Box5, { sx: { display: "flex", justifyContent: "flex-end", mt: 1 } }, /* @__PURE__ */ React5.createElement(
2249
+ Button4,
2250
+ {
2251
+ onClick: handleSaveMcpServers,
2252
+ disabled: mcpBusy,
2253
+ variant: "outlined"
2254
+ },
2255
+ mcpBusy ? "Saving\u2026" : "Save MCP servers"
2256
+ )))), /* @__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")));
2257
+ };
2258
+ }
2259
+ });
2260
+
2261
+ // src/components/UsageStats.tsx
2262
+ import React6, { useMemo as useMemo4, useState as useState4 } from "react";
2263
+ import Paper3 from "@mui/material/Paper";
2264
+ import Box6 from "@mui/material/Box";
2265
+ import Typography6 from "@mui/material/Typography";
2266
+ import TextField4 from "@mui/material/TextField";
1866
2267
  import MenuItem2 from "@mui/material/MenuItem";
1867
2268
  import Grid from "@mui/material/Grid";
1868
2269
  import Table2 from "@mui/material/Table";
@@ -1908,17 +2309,17 @@ var init_UsageStats = __esm({
1908
2309
  "30d": "Last 30 days"
1909
2310
  };
1910
2311
  fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
1911
- ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React5.createElement(Skeleton3, { variant: "rounded", height });
2312
+ ChartSkeleton = ({ height = 240 }) => /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height });
1912
2313
  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);
2314
+ if (loading) return /* @__PURE__ */ React6.createElement(ChartSkeleton, { height });
2315
+ if (empty) return /* @__PURE__ */ React6.createElement(EmptyState, { message: "No data for this period", height });
2316
+ return /* @__PURE__ */ React6.createElement(React6.Fragment, null, children);
1916
2317
  };
1917
2318
  SuccessRateCell = ({ rate, requests }) => {
1918
- if (requests === 0) return /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary" }, "\u2014");
2319
+ if (requests === 0) return /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "\u2014");
1919
2320
  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,
2321
+ 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(
2322
+ Typography6,
1922
2323
  {
1923
2324
  variant: "caption",
1924
2325
  sx: { fontVariantNumeric: "tabular-nums", minWidth: 44 }
@@ -1936,8 +2337,8 @@ var init_UsageStats = __esm({
1936
2337
  userInfo
1937
2338
  }) => {
1938
2339
  const chart = useChartTheme();
1939
- const [selectedModel, setSelectedModel] = useState3("all");
1940
- const [tab, setTab] = useState3("costs");
2340
+ const [selectedModel, setSelectedModel] = useState4("all");
2341
+ const [tab, setTab] = useState4("costs");
1941
2342
  const handlePresetChange = (preset) => {
1942
2343
  const end = /* @__PURE__ */ new Date();
1943
2344
  const start = /* @__PURE__ */ new Date();
@@ -2066,24 +2467,24 @@ var init_UsageStats = __esm({
2066
2467
  ];
2067
2468
  const renderKeyRows = () => {
2068
2469
  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 })));
2470
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2070
2471
  }
2071
2472
  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" })));
2473
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No key activity in this period" })));
2073
2474
  }
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 }))));
2475
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.keyAlias), r.teamId ? /* @__PURE__ */ React6.createElement(Typography6, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.slice(0, 3).map((m) => /* @__PURE__ */ React6.createElement(TagChip, { key: m, label: m, title: m })), r.models.length > 3 && /* @__PURE__ */ React6.createElement(TagChip, { label: `+${r.models.length - 3}`, mono: false }))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.totalTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2075
2476
  };
2076
2477
  const renderModelRows = () => {
2077
2478
  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 })));
2479
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8, sx: { py: 3 } }, /* @__PURE__ */ React6.createElement(Skeleton3, { variant: "rounded", height: 80 })));
2079
2480
  }
2080
2481
  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" })));
2482
+ return /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React6.createElement(EmptyState, { message: "No model activity in this period" })));
2082
2483
  }
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 }))));
2484
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React6.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(Box6, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React6.createElement(Box6, { sx: { width: 8, height: 8, borderRadius: "50%", bgcolor: chartColor(r.model), flexShrink: 0 } }), /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", sx: { fontWeight: 600 } }, r.model))), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.promptTokens)), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, fmtCompact(r.completionTokens)), /* @__PURE__ */ React6.createElement(TableCell2, null, /* @__PURE__ */ React6.createElement(SuccessRateCell, { rate: r.successRate, requests: r.apiRequests }))));
2084
2485
  };
2085
- const periodSelect = /* @__PURE__ */ React5.createElement(
2086
- TextField3,
2486
+ const periodSelect = /* @__PURE__ */ React6.createElement(
2487
+ TextField4,
2087
2488
  {
2088
2489
  select: true,
2089
2490
  size: "small",
@@ -2092,18 +2493,18 @@ var init_UsageStats = __esm({
2092
2493
  onChange: (e) => handlePresetChange(e.target.value),
2093
2494
  sx: { minWidth: 160 }
2094
2495
  },
2095
- Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React5.createElement(MenuItem2, { key: p, value: p }, PRESET_LABELS[p]))
2496
+ Object.keys(PRESET_LABELS).map((p) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: p, value: p }, PRESET_LABELS[p]))
2096
2497
  );
2097
- return /* @__PURE__ */ React5.createElement(
2498
+ return /* @__PURE__ */ React6.createElement(
2098
2499
  SectionCard,
2099
2500
  {
2100
2501
  title: "Usage Analytics",
2101
2502
  subtitle: `${PRESET_LABELS[currentPreset]} \xB7 ${dateRange.start.toLocaleDateString()} \u2013 ${dateRange.end.toLocaleDateString()}`,
2102
2503
  actions: periodSelect
2103
2504
  },
2104
- /* @__PURE__ */ React5.createElement(MetricStrip, { metrics }),
2105
- /* @__PURE__ */ React5.createElement(
2106
- Box5,
2505
+ /* @__PURE__ */ React6.createElement(MetricStrip, { metrics }),
2506
+ /* @__PURE__ */ React6.createElement(
2507
+ Box6,
2107
2508
  {
2108
2509
  sx: {
2109
2510
  display: "flex",
@@ -2115,7 +2516,7 @@ var init_UsageStats = __esm({
2115
2516
  mb: 2
2116
2517
  }
2117
2518
  },
2118
- /* @__PURE__ */ React5.createElement(
2519
+ /* @__PURE__ */ React6.createElement(
2119
2520
  SegmentedControl,
2120
2521
  {
2121
2522
  value: tab,
@@ -2127,8 +2528,8 @@ var init_UsageStats = __esm({
2127
2528
  ]
2128
2529
  }
2129
2530
  ),
2130
- tab === "models" && /* @__PURE__ */ React5.createElement(
2131
- TextField3,
2531
+ tab === "models" && /* @__PURE__ */ React6.createElement(
2532
+ TextField4,
2132
2533
  {
2133
2534
  select: true,
2134
2535
  size: "small",
@@ -2137,17 +2538,17 @@ var init_UsageStats = __esm({
2137
2538
  onChange: (e) => setSelectedModel(e.target.value),
2138
2539
  sx: { minWidth: 220 }
2139
2540
  },
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))
2541
+ /* @__PURE__ */ React6.createElement(MenuItem2, { value: "all" }, "All models"),
2542
+ models.map((m) => /* @__PURE__ */ React6.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
2142
2543
  )
2143
2544
  ),
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(
2545
+ 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
2546
  Tooltip,
2146
2547
  {
2147
2548
  cursor: chart.cursor,
2148
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2549
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd, hideZero: true })
2149
2550
  }
2150
- ), topSpendModels.map((m) => /* @__PURE__ */ React5.createElement(
2551
+ ), topSpendModels.map((m) => /* @__PURE__ */ React6.createElement(
2151
2552
  Area,
2152
2553
  {
2153
2554
  key: m,
@@ -2159,13 +2560,13 @@ var init_UsageStats = __esm({
2159
2560
  fill: chartColor(m),
2160
2561
  fillOpacity: 0.28
2161
2562
  }
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(
2563
+ ))))), !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
2564
  Tooltip,
2164
2565
  {
2165
2566
  cursor: chart.cursor,
2166
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2567
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: (v) => `${v}%` })
2167
2568
  }
2168
- ), /* @__PURE__ */ React5.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React5.createElement(
2569
+ ), /* @__PURE__ */ React6.createElement(ReferenceLine, { y: 100, stroke: SERIES.success, strokeDasharray: "4 4", strokeOpacity: 0.5 }), /* @__PURE__ */ React6.createElement(
2169
2570
  Line,
2170
2571
  {
2171
2572
  type: "monotone",
@@ -2176,13 +2577,13 @@ var init_UsageStats = __esm({
2176
2577
  dot: { r: 2.5, strokeWidth: 0, fill: SERIES.input },
2177
2578
  activeDot: { r: 4 }
2178
2579
  }
2179
- )))))), /* @__PURE__ */ React5.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React5.createElement(
2580
+ )))))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React6.createElement(
2180
2581
  ChartCard,
2181
2582
  {
2182
2583
  title: maxBudget > 0 ? "Cumulative spend vs budget" : "Cumulative spend",
2183
2584
  meta: maxBudget > 0 ? `${fmtUsd(totalCumSpend)} used \xB7 ${fmtUsd(Math.max(0, maxBudget - totalCumSpend))} left` : void 0
2184
2585
  },
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(
2586
+ /* @__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
2587
  YAxis,
2187
2588
  {
2188
2589
  tickFormatter: fmtUsdCompact,
@@ -2190,13 +2591,13 @@ var init_UsageStats = __esm({
2190
2591
  domain: cumulativeDomain,
2191
2592
  ...chart.axis
2192
2593
  }
2193
- ), /* @__PURE__ */ React5.createElement(
2594
+ ), /* @__PURE__ */ React6.createElement(
2194
2595
  Tooltip,
2195
2596
  {
2196
2597
  cursor: chart.cursor,
2197
- content: /* @__PURE__ */ React5.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2598
+ content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd })
2198
2599
  }
2199
- ), maxBudget > 0 && /* @__PURE__ */ React5.createElement(
2600
+ ), maxBudget > 0 && /* @__PURE__ */ React6.createElement(
2200
2601
  ReferenceLine,
2201
2602
  {
2202
2603
  y: maxBudget,
@@ -2204,7 +2605,7 @@ var init_UsageStats = __esm({
2204
2605
  strokeDasharray: "5 4",
2205
2606
  label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 10, fill: SERIES.budget }
2206
2607
  }
2207
- ), /* @__PURE__ */ React5.createElement(
2608
+ ), /* @__PURE__ */ React6.createElement(
2208
2609
  Area,
2209
2610
  {
2210
2611
  type: "monotone",
@@ -2217,44 +2618,44 @@ var init_UsageStats = __esm({
2217
2618
  }
2218
2619
  ))))
2219
2620
  ))),
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(
2621
+ 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
2622
  ChartOrFallback,
2222
2623
  {
2223
2624
  loading,
2224
2625
  empty: topModelSpendBars.length === 0,
2225
2626
  height: Math.max(200, topModelSpendBars.length * 34)
2226
2627
  },
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(
2628
+ /* @__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) })))))
2629
+ ))), /* @__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
2630
  ChartOrFallback,
2230
2631
  {
2231
2632
  loading,
2232
2633
  empty: modelRows.length === 0,
2233
2634
  height: Math.max(200, topModelSpendBars.length * 34)
2234
2635
  },
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(
2636
+ /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "model", width: 170, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtInt, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Legend, { ...chart.legend }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "promptTokens", name: "Input", fill: SERIES.input, stackId: "t", barSize: 14 }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "completionTokens", name: "Output", fill: SERIES.output, stackId: "t", barSize: 14, radius: [0, 3, 3, 0] })))
2637
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Input"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Output"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderModelRows()))))),
2638
+ 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
2639
  ChartOrFallback,
2239
2640
  {
2240
2641
  loading,
2241
2642
  empty: topKeySpendBars.length === 0,
2242
2643
  height: Math.max(160, topKeySpendBars.length * 32)
2243
2644
  },
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())))))
2645
+ /* @__PURE__ */ React6.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React6.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { top: 0, right: 12, left: 0, bottom: 0 } }, /* @__PURE__ */ React6.createElement(CartesianGrid, { ...chart.grid, vertical: true, horizontal: false }), /* @__PURE__ */ React6.createElement(XAxis, { type: "number", tickFormatter: fmtUsdCompact, ...chart.axis }), /* @__PURE__ */ React6.createElement(YAxis, { type: "category", dataKey: "keyAlias", width: 160, ...chart.axis, tick: { fontSize: 10.5, fill: chart.axis.tick.fill } }), /* @__PURE__ */ React6.createElement(Tooltip, { cursor: chart.cursor, content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: fmtUsd, labelFormatter: (l) => l }) }), /* @__PURE__ */ React6.createElement(Bar, { dataKey: "spend", name: "Spend", fill: SERIES.input, radius: [0, 3, 3, 0], barSize: 14 })))
2646
+ ))), /* @__PURE__ */ React6.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React6.createElement(TableContainer2, { component: Paper3, variant: "outlined", sx: { borderRadius: 2 } }, /* @__PURE__ */ React6.createElement(Table2, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React6.createElement(TableHead2, null, /* @__PURE__ */ React6.createElement(TableRow2, null, /* @__PURE__ */ React6.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React6.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React6.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React6.createElement(TableCell2, { sx: { width: 160 } }, "Success rate"))), /* @__PURE__ */ React6.createElement(TableBody2, null, renderKeyRows())))))
2246
2647
  );
2247
2648
  };
2248
2649
  }
2249
2650
  });
2250
2651
 
2251
2652
  // src/components/TeamUsage.tsx
2252
- import React6, { useState as useState4 } from "react";
2653
+ import React7, { useState as useState5 } from "react";
2253
2654
  import Paper4 from "@mui/material/Paper";
2254
- import Box6 from "@mui/material/Box";
2255
- import Typography6 from "@mui/material/Typography";
2655
+ import Box7 from "@mui/material/Box";
2656
+ import Typography7 from "@mui/material/Typography";
2256
2657
  import Skeleton4 from "@mui/material/Skeleton";
2257
- import Divider2 from "@mui/material/Divider";
2658
+ import Divider3 from "@mui/material/Divider";
2258
2659
  import Table3 from "@mui/material/Table";
2259
2660
  import TableBody3 from "@mui/material/TableBody";
2260
2661
  import TableCell3 from "@mui/material/TableCell";
@@ -2262,7 +2663,8 @@ import TableHead3 from "@mui/material/TableHead";
2262
2663
  import TableRow3 from "@mui/material/TableRow";
2263
2664
  import TableContainer3 from "@mui/material/TableContainer";
2264
2665
  import Collapse from "@mui/material/Collapse";
2265
- import IconButton3 from "@mui/material/IconButton";
2666
+ import IconButton4 from "@mui/material/IconButton";
2667
+ import Button5 from "@mui/material/Button";
2266
2668
  import CircularProgress3 from "@mui/material/CircularProgress";
2267
2669
  import { alpha as alpha4 } from "@mui/material/styles";
2268
2670
  import { ExpandMore as ExpandMore2, Group } from "@mui/icons-material";
@@ -2286,8 +2688,8 @@ var init_TeamUsage = __esm({
2286
2688
  "use strict";
2287
2689
  init_ui();
2288
2690
  fmtUsd2 = (n) => `$${(n ?? 0).toFixed(2)}`;
2289
- TeamCard = ({ team, usage, usageLoading }) => {
2290
- const [expanded, setExpanded] = useState4(false);
2691
+ TeamCard = ({ team, usage, usageLoading, canManage, onEditTeam }) => {
2692
+ const [expanded, setExpanded] = useState5(false);
2291
2693
  const chart = useChartTheme();
2292
2694
  const budget = team.max_budget ?? 0;
2293
2695
  const spend = team.spend ?? 0;
@@ -2297,24 +2699,24 @@ var init_TeamUsage = __esm({
2297
2699
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
2298
2700
  const renderDailySpendSection = () => {
2299
2701
  if (usageLoading) {
2300
- return /* @__PURE__ */ React6.createElement(Box6, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React6.createElement(CircularProgress3, { size: 22 }));
2702
+ return /* @__PURE__ */ React7.createElement(Box7, { display: "flex", justifyContent: "center", py: 3 }, /* @__PURE__ */ React7.createElement(CircularProgress3, { size: 22 }));
2301
2703
  }
2302
2704
  if (dailyData.length === 0) return null;
2303
- return /* @__PURE__ */ React6.createElement(Box6, { mb: 2.5 }, /* @__PURE__ */ React6.createElement(
2304
- Typography6,
2705
+ return /* @__PURE__ */ React7.createElement(Box7, { mb: 2.5 }, /* @__PURE__ */ React7.createElement(
2706
+ Typography7,
2305
2707
  {
2306
2708
  variant: "caption",
2307
2709
  color: "text.secondary",
2308
2710
  sx: { display: "block", fontSize: 11, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase", mb: 1 }
2309
2711
  },
2310
2712
  "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(
2713
+ ), /* @__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
2714
  Tooltip2,
2313
2715
  {
2314
2716
  cursor: chart.cursor,
2315
- content: /* @__PURE__ */ React6.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
2717
+ content: /* @__PURE__ */ React7.createElement(ChartTooltip, { valueFormatter: (v) => `$${v.toFixed(4)}` })
2316
2718
  }
2317
- ), /* @__PURE__ */ React6.createElement(
2719
+ ), /* @__PURE__ */ React7.createElement(
2318
2720
  Area2,
2319
2721
  {
2320
2722
  type: "monotone",
@@ -2327,8 +2729,8 @@ var init_TeamUsage = __esm({
2327
2729
  }
2328
2730
  )))));
2329
2731
  };
2330
- const sectionLabel = (label) => /* @__PURE__ */ React6.createElement(
2331
- Typography6,
2732
+ const sectionLabel = (label) => /* @__PURE__ */ React7.createElement(
2733
+ Typography7,
2332
2734
  {
2333
2735
  variant: "caption",
2334
2736
  color: "text.secondary",
@@ -2336,8 +2738,8 @@ var init_TeamUsage = __esm({
2336
2738
  },
2337
2739
  label
2338
2740
  );
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,
2741
+ return /* @__PURE__ */ React7.createElement(Paper4, { variant: "outlined", sx: { borderRadius: 2, p: 2.5 } }, /* @__PURE__ */ React7.createElement(Box7, { display: "flex", alignItems: "center", gap: 1.5 }, /* @__PURE__ */ React7.createElement(
2742
+ Box7,
2341
2743
  {
2342
2744
  sx: (theme) => ({
2343
2745
  width: 36,
@@ -2351,17 +2753,26 @@ var init_TeamUsage = __esm({
2351
2753
  color: theme.palette.primary.main
2352
2754
  })
2353
2755
  },
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,
2756
+ /* @__PURE__ */ React7.createElement(Group, { fontSize: "small" })
2757
+ ), /* @__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(
2758
+ Typography7,
2357
2759
  {
2358
2760
  variant: "caption",
2359
2761
  color: "text.secondary",
2360
2762
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2361
2763
  },
2362
2764
  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,
2765
+ )), 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(
2766
+ Button5,
2767
+ {
2768
+ size: "small",
2769
+ variant: "text",
2770
+ onClick: () => onEditTeam(team),
2771
+ sx: { textTransform: "none" }
2772
+ },
2773
+ "Edit"
2774
+ ), /* @__PURE__ */ React7.createElement(
2775
+ IconButton4,
2365
2776
  {
2366
2777
  size: "small",
2367
2778
  onClick: () => setExpanded((e) => !e),
@@ -2373,9 +2784,9 @@ var init_TeamUsage = __esm({
2373
2784
  transition: theme.transitions.create("transform")
2374
2785
  })
2375
2786
  },
2376
- /* @__PURE__ */ React6.createElement(ExpandMore2, null)
2377
- )), /* @__PURE__ */ React6.createElement(
2378
- Box6,
2787
+ /* @__PURE__ */ React7.createElement(ExpandMore2, null)
2788
+ )), /* @__PURE__ */ React7.createElement(
2789
+ Box7,
2379
2790
  {
2380
2791
  sx: {
2381
2792
  display: "grid",
@@ -2385,35 +2796,35 @@ var init_TeamUsage = __esm({
2385
2796
  maxWidth: 640
2386
2797
  }
2387
2798
  },
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(
2799
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Members", value: team.members_with_roles?.length ?? "\u2014" }),
2800
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Models", value: team.models?.length ? team.models.length : "All" }),
2801
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Budget", value: budget > 0 ? fmtUsd2(budget) : "Unlimited" }),
2802
+ /* @__PURE__ */ React7.createElement(Stat, { label: "Spend", value: fmtUsd2(spend) }),
2803
+ /* @__PURE__ */ React7.createElement(Stat, { label: "TPM", value: team.tpm_limit && team.tpm_limit > 0 ? team.tpm_limit.toLocaleString() : "\u2014" }),
2804
+ /* @__PURE__ */ React7.createElement(Stat, { label: "RPM", value: team.rpm_limit && team.rpm_limit > 0 ? team.rpm_limit.toLocaleString() : "\u2014" })
2805
+ ), 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
2806
  TableContainer3,
2396
2807
  {
2397
2808
  component: Paper4,
2398
2809
  variant: "outlined",
2399
2810
  sx: { borderRadius: 1.5 }
2400
2811
  },
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,
2812
+ /* @__PURE__ */ React7.createElement(Table3, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React7.createElement(TableHead3, null, /* @__PURE__ */ React7.createElement(TableRow3, null, /* @__PURE__ */ React7.createElement(TableCell3, null, "User"), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, "Role"))), /* @__PURE__ */ React7.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React7.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React7.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2" }, m.user_email), /* @__PURE__ */ React7.createElement(
2813
+ Typography7,
2403
2814
  {
2404
2815
  variant: "caption",
2405
2816
  color: "text.secondary",
2406
2817
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 }
2407
2818
  },
2408
2819
  m.user_id
2409
- )) : /* @__PURE__ */ React6.createElement(
2410
- Typography6,
2820
+ )) : /* @__PURE__ */ React7.createElement(
2821
+ Typography7,
2411
2822
  {
2412
2823
  variant: "body2",
2413
2824
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
2414
2825
  },
2415
2826
  m.user_id
2416
- )), /* @__PURE__ */ React6.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React6.createElement(
2827
+ )), /* @__PURE__ */ React7.createElement(TableCell3, { align: "right" }, /* @__PURE__ */ React7.createElement(
2417
2828
  StatusPill,
2418
2829
  {
2419
2830
  label: m.role,
@@ -2421,19 +2832,21 @@ var init_TeamUsage = __esm({
2421
2832
  dot: false
2422
2833
  }
2423
2834
  ))))))
2424
- ) : /* @__PURE__ */ React6.createElement(Typography6, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
2835
+ ) : /* @__PURE__ */ React7.createElement(Typography7, { variant: "body2", color: "text.secondary" }, "No members assigned."))));
2425
2836
  };
2426
2837
  TeamUsage = ({
2427
2838
  teams,
2428
2839
  loading,
2429
2840
  getTeamUsage,
2430
- getTeamUsageLoading
2841
+ getTeamUsageLoading,
2842
+ canManage,
2843
+ onEditTeam
2431
2844
  }) => {
2432
2845
  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 }));
2846
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120, sx: { mb: 2 } }), /* @__PURE__ */ React7.createElement(Skeleton4, { variant: "rounded", height: 120 }));
2434
2847
  }
2435
2848
  if (!teams.length) {
2436
- return /* @__PURE__ */ React6.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React6.createElement(
2849
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams" }, /* @__PURE__ */ React7.createElement(
2437
2850
  EmptyState,
2438
2851
  {
2439
2852
  message: "You're not a member of any LiteLLM team yet.",
@@ -2441,13 +2854,15 @@ var init_TeamUsage = __esm({
2441
2854
  }
2442
2855
  ));
2443
2856
  }
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(
2857
+ return /* @__PURE__ */ React7.createElement(SectionCard, { title: "Teams", subtitle: `${teams.length} team${teams.length === 1 ? "" : "s"}` }, /* @__PURE__ */ React7.createElement(Box7, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teams.map((team) => /* @__PURE__ */ React7.createElement(
2445
2858
  TeamCard,
2446
2859
  {
2447
2860
  key: team.team_id,
2448
2861
  team,
2449
2862
  usage: getTeamUsage(team.team_id),
2450
- usageLoading: getTeamUsageLoading(team.team_id)
2863
+ usageLoading: getTeamUsageLoading(team.team_id),
2864
+ canManage,
2865
+ onEditTeam
2451
2866
  }
2452
2867
  ))));
2453
2868
  };
@@ -2455,15 +2870,15 @@ var init_TeamUsage = __esm({
2455
2870
  });
2456
2871
 
2457
2872
  // src/components/ModelsTable.tsx
2458
- import React7, { useState as useState5, useMemo as useMemo5, useCallback } from "react";
2459
- import Box7 from "@mui/material/Box";
2873
+ import React8, { useState as useState6, useMemo as useMemo5, useCallback } from "react";
2874
+ import Box8 from "@mui/material/Box";
2460
2875
  import Table4 from "@mui/material/Table";
2461
2876
  import TableBody4 from "@mui/material/TableBody";
2462
2877
  import TableCell4 from "@mui/material/TableCell";
2463
2878
  import TableContainer4 from "@mui/material/TableContainer";
2464
2879
  import TableHead4 from "@mui/material/TableHead";
2465
2880
  import TableRow4 from "@mui/material/TableRow";
2466
- import Typography7 from "@mui/material/Typography";
2881
+ import Typography8 from "@mui/material/Typography";
2467
2882
  import MenuItem3 from "@mui/material/MenuItem";
2468
2883
  import Select from "@mui/material/Select";
2469
2884
  import FormControl from "@mui/material/FormControl";
@@ -2471,7 +2886,7 @@ import InputLabel from "@mui/material/InputLabel";
2471
2886
  import ContentCopyIcon from "@mui/icons-material/ContentCopy";
2472
2887
  import Tooltip3 from "@mui/material/Tooltip";
2473
2888
  import Snackbar from "@mui/material/Snackbar";
2474
- import Alert2 from "@mui/material/Alert";
2889
+ import Alert3 from "@mui/material/Alert";
2475
2890
  import { alpha as alpha5, useTheme as useTheme2 } from "@mui/material/styles";
2476
2891
  function isModelAllowedByTeam2(model, teamModels) {
2477
2892
  if (!teamModels || teamModels.length === 0 || teamModels.includes(ALL_PROXY_MODELS2)) return true;
@@ -2498,8 +2913,8 @@ var init_ModelsTable = __esm({
2498
2913
  ALL_PROXY_MODELS2 = "all-proxy-models";
2499
2914
  ModelsTable = ({ allModels, teams, loading }) => {
2500
2915
  const theme = useTheme2();
2501
- const [selectedTeamId, setSelectedTeamId] = useState5("");
2502
- const [copiedSnackbar, setCopiedSnackbar] = useState5(false);
2916
+ const [selectedTeamId, setSelectedTeamId] = useState6("");
2917
+ const [copiedSnackbar, setCopiedSnackbar] = useState6(false);
2503
2918
  const selectedTeam = useMemo5(
2504
2919
  () => teams.find((t) => t.team_id === selectedTeamId) ?? null,
2505
2920
  [teams, selectedTeamId]
@@ -2525,13 +2940,13 @@ var init_ModelsTable = __esm({
2525
2940
  return theme.palette.info.main;
2526
2941
  }
2527
2942
  };
2528
- return /* @__PURE__ */ React7.createElement(
2943
+ return /* @__PURE__ */ React8.createElement(
2529
2944
  SectionCard,
2530
2945
  {
2531
2946
  title: "Available Models",
2532
2947
  subtitle: selectedTeam ? `${filteredModels.length} model${filteredModels.length !== 1 ? "s" : ""} in ${selectedTeam.team_alias ?? selectedTeam.team_id}` : "Select a team to view its models"
2533
2948
  },
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(
2949
+ /* @__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
2950
  Select,
2536
2951
  {
2537
2952
  labelId: "team-select-label",
@@ -2539,25 +2954,25 @@ var init_ModelsTable = __esm({
2539
2954
  label: "Team",
2540
2955
  onChange: (e) => setSelectedTeamId(e.target.value)
2541
2956
  },
2542
- teams.map((t) => /* @__PURE__ */ React7.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2957
+ teams.map((t) => /* @__PURE__ */ React8.createElement(MenuItem3, { key: t.team_id, value: t.team_id }, t.team_alias ?? t.team_id))
2543
2958
  ))),
2544
- loading && /* @__PURE__ */ React7.createElement(EmptyState, { message: "Loading models\u2026" }),
2545
- !loading && !selectedTeamId && /* @__PURE__ */ React7.createElement(
2959
+ loading && /* @__PURE__ */ React8.createElement(EmptyState, { message: "Loading models\u2026" }),
2960
+ !loading && !selectedTeamId && /* @__PURE__ */ React8.createElement(
2546
2961
  EmptyState,
2547
2962
  {
2548
2963
  message: "Select a team to see available models",
2549
2964
  hint: "Each team may have a different set of models assigned"
2550
2965
  }
2551
2966
  ),
2552
- !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React7.createElement(
2967
+ !loading && selectedTeamId && filteredModels.length === 0 && /* @__PURE__ */ React8.createElement(
2553
2968
  EmptyState,
2554
2969
  {
2555
2970
  message: "No models available for this team",
2556
2971
  hint: allModels.length === 0 ? "No models are configured in the system" : "None of the team's assigned models were found in the catalog"
2557
2972
  }
2558
2973
  ),
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,
2974
+ !loading && filteredModels.length > 0 && /* @__PURE__ */ React8.createElement(TableContainer4, null, /* @__PURE__ */ React8.createElement(Table4, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React8.createElement(TableHead4, null, /* @__PURE__ */ React8.createElement(TableRow4, null, /* @__PURE__ */ React8.createElement(TableCell4, null, "Model ID"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Mode"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Input Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Output Cost"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Input"), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, "Max Output"), /* @__PURE__ */ React8.createElement(TableCell4, null, "Capabilities"))), /* @__PURE__ */ React8.createElement(TableBody4, null, filteredModels.map((m) => /* @__PURE__ */ React8.createElement(TableRow4, { key: m.model_name, hover: true }, /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", alignItems: "center", gap: 0.5 } }, /* @__PURE__ */ React8.createElement(TagChip, { label: m.model_name, title: m.model_name }), /* @__PURE__ */ React8.createElement(Tooltip3, { title: "Copy model ID" }, /* @__PURE__ */ React8.createElement(
2975
+ Box8,
2561
2976
  {
2562
2977
  component: "span",
2563
2978
  role: "button",
@@ -2567,9 +2982,9 @@ var init_ModelsTable = __esm({
2567
2982
  sx: quietIconButtonSx("accent"),
2568
2983
  style: { cursor: "pointer", display: "inline-flex" }
2569
2984
  },
2570
- /* @__PURE__ */ React7.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
2571
- )))), /* @__PURE__ */ React7.createElement(TableCell4, null, /* @__PURE__ */ React7.createElement(
2572
- Box7,
2985
+ /* @__PURE__ */ React8.createElement(ContentCopyIcon, { sx: { fontSize: 14 } })
2986
+ )))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(
2987
+ Box8,
2573
2988
  {
2574
2989
  component: "span",
2575
2990
  sx: {
@@ -2585,8 +3000,8 @@ var init_ModelsTable = __esm({
2585
3000
  }
2586
3001
  },
2587
3002
  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(
3003
+ )), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.input_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtCostPerToken(m.output_cost_per_token))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_input_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, { align: "right" }, /* @__PURE__ */ React8.createElement(Typography8, { variant: "body2", sx: { fontVariantNumeric: "tabular-nums" } }, fmtTokens(m.max_output_tokens))), /* @__PURE__ */ React8.createElement(TableCell4, null, /* @__PURE__ */ React8.createElement(Box8, { sx: { display: "flex", gap: 0.5, flexWrap: "wrap" } }, m.supports_function_calling && /* @__PURE__ */ React8.createElement(TagChip, { label: "Fn", title: "Function calling" }), m.supports_vision && /* @__PURE__ */ React8.createElement(TagChip, { label: "\u{1F441}", title: "Vision" })))))))),
3004
+ /* @__PURE__ */ React8.createElement(
2590
3005
  Snackbar,
2591
3006
  {
2592
3007
  open: copiedSnackbar,
@@ -2594,7 +3009,7 @@ var init_ModelsTable = __esm({
2594
3009
  onClose: () => setCopiedSnackbar(false),
2595
3010
  anchorOrigin: { vertical: "bottom", horizontal: "center" }
2596
3011
  },
2597
- /* @__PURE__ */ React7.createElement(Alert2, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
3012
+ /* @__PURE__ */ React8.createElement(Alert3, { severity: "success", variant: "filled", sx: { fontSize: 13 } }, "Model ID copied to clipboard")
2598
3013
  )
2599
3014
  );
2600
3015
  };
@@ -2602,9 +3017,9 @@ var init_ModelsTable = __esm({
2602
3017
  });
2603
3018
 
2604
3019
  // 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";
3020
+ import React9, { useState as useState7, useCallback as useCallback2 } from "react";
3021
+ import Box9 from "@mui/material/Box";
3022
+ import Typography9 from "@mui/material/Typography";
2608
3023
  import Table5 from "@mui/material/Table";
2609
3024
  import TableBody5 from "@mui/material/TableBody";
2610
3025
  import TableCell5 from "@mui/material/TableCell";
@@ -2612,12 +3027,12 @@ import TableContainer5 from "@mui/material/TableContainer";
2612
3027
  import TableHead5 from "@mui/material/TableHead";
2613
3028
  import TableRow5 from "@mui/material/TableRow";
2614
3029
  import TablePagination from "@mui/material/TablePagination";
2615
- import TextField4 from "@mui/material/TextField";
3030
+ import TextField5 from "@mui/material/TextField";
2616
3031
  import MenuItem4 from "@mui/material/MenuItem";
2617
3032
  import Skeleton5 from "@mui/material/Skeleton";
2618
3033
  import Collapse2 from "@mui/material/Collapse";
2619
- import IconButton4 from "@mui/material/IconButton";
2620
- import Alert3 from "@mui/material/Alert";
3034
+ import IconButton5 from "@mui/material/IconButton";
3035
+ import Alert4 from "@mui/material/Alert";
2621
3036
  import { alpha as alpha6 } from "@mui/material/styles";
2622
3037
  import { KeyboardArrowDown } from "@mui/icons-material";
2623
3038
  import { useAsync } from "react-use";
@@ -2641,10 +3056,10 @@ function formatDateTime(iso) {
2641
3056
  }
2642
3057
  function renderAuditLogBody(loading, entries) {
2643
3058
  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 })));
3059
+ return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { py: 2 } }, /* @__PURE__ */ React9.createElement(Skeleton5, { variant: "rounded", height: 160 })));
2645
3060
  }
2646
3061
  if (entries.length === 0) {
2647
- return /* @__PURE__ */ React8.createElement(TableRow5, null, /* @__PURE__ */ React8.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React8.createElement(
3062
+ return /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6 }, /* @__PURE__ */ React9.createElement(
2648
3063
  EmptyState,
2649
3064
  {
2650
3065
  message: "No audit events found",
@@ -2652,7 +3067,7 @@ function renderAuditLogBody(loading, entries) {
2652
3067
  }
2653
3068
  )));
2654
3069
  }
2655
- return entries.map((entry) => /* @__PURE__ */ React8.createElement(DetailRow, { key: entry.id, entry }));
3070
+ return entries.map((entry) => /* @__PURE__ */ React9.createElement(DetailRow, { key: entry.id, entry }));
2656
3071
  }
2657
3072
  var ACTION_TONES, TABLE_LABELS, DetailRow, AuditLog;
2658
3073
  var init_AuditLog = __esm({
@@ -2669,13 +3084,15 @@ var init_AuditLog = __esm({
2669
3084
  TABLE_LABELS = {
2670
3085
  LiteLLM_VerificationToken: "Key",
2671
3086
  LiteLLM_TeamTable: "Team",
3087
+ LiteLLM_TeamMembership: "Team member",
3088
+ LiteLLM_ObjectPermissionTable: "Team access (KB / MCP)",
2672
3089
  LiteLLM_UserTable: "User"
2673
3090
  };
2674
3091
  DetailRow = ({ entry }) => {
2675
- const [open, setOpen] = useState6(false);
3092
+ const [open, setOpen] = useState7(false);
2676
3093
  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,
3094
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sx: { width: 40, pr: 0 } }, hasDetail && /* @__PURE__ */ React9.createElement(
3095
+ IconButton5,
2679
3096
  {
2680
3097
  size: "small",
2681
3098
  onClick: () => setOpen((o) => !o),
@@ -2687,9 +3104,9 @@ var init_AuditLog = __esm({
2687
3104
  transition: theme.transitions.create("transform")
2688
3105
  })
2689
3106
  },
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,
3107
+ /* @__PURE__ */ React9.createElement(KeyboardArrowDown, { fontSize: "small" })
3108
+ )), /* @__PURE__ */ React9.createElement(TableCell5, { sx: { whiteSpace: "nowrap" } }, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2", color: "text.secondary" }, formatDateTime(entry.updated_at))), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.action && /* @__PURE__ */ React9.createElement(StatusPill, { label: entry.action, tone: actionTone(entry.action) })), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(Typography9, { variant: "body2" }, prettyTableName(entry.table_name))), /* @__PURE__ */ React9.createElement(TableCell5, null, /* @__PURE__ */ React9.createElement(
3109
+ Typography9,
2693
3110
  {
2694
3111
  variant: "body2",
2695
3112
  component: "code",
@@ -2698,8 +3115,8 @@ var init_AuditLog = __esm({
2698
3115
  sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 12 }
2699
3116
  },
2700
3117
  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,
3118
+ )), /* @__PURE__ */ React9.createElement(TableCell5, null, entry.changed_by ?? "\u2014")), hasDetail && /* @__PURE__ */ React9.createElement(TableRow5, { sx: { "&&:hover": { bgcolor: "transparent" } } }, /* @__PURE__ */ React9.createElement(TableCell5, { colSpan: 6, sx: { "&&": { py: 0, border: 0 } } }, /* @__PURE__ */ React9.createElement(Collapse2, { in: open, unmountOnExit: true }, /* @__PURE__ */ React9.createElement(
3119
+ Box9,
2703
3120
  {
2704
3121
  display: "flex",
2705
3122
  gap: 2,
@@ -2711,8 +3128,8 @@ var init_AuditLog = __esm({
2711
3128
  bgcolor: alpha6(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.05 : 0.03)
2712
3129
  })
2713
3130
  },
2714
- entry.before_value && /* @__PURE__ */ React8.createElement(Box8, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React8.createElement(
2715
- Typography8,
3131
+ entry.before_value && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3132
+ Typography9,
2716
3133
  {
2717
3134
  variant: "caption",
2718
3135
  color: "text.secondary",
@@ -2721,9 +3138,9 @@ var init_AuditLog = __esm({
2721
3138
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2722
3139
  },
2723
3140
  "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,
3141
+ ), /* @__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))),
3142
+ entry.updated_values && /* @__PURE__ */ React9.createElement(Box9, { flex: 1, minWidth: 200 }, /* @__PURE__ */ React9.createElement(
3143
+ Typography9,
2727
3144
  {
2728
3145
  variant: "caption",
2729
3146
  color: "text.secondary",
@@ -2732,13 +3149,13 @@ var init_AuditLog = __esm({
2732
3149
  sx: { fontSize: 10.5, fontWeight: 700, letterSpacing: "0.07em", textTransform: "uppercase" }
2733
3150
  },
2734
3151
  "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)))
3152
+ ), /* @__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
3153
  )))));
2737
3154
  };
2738
3155
  AuditLog = ({ api }) => {
2739
- const [page, setPage] = useState6(0);
2740
- const [pageSize, setPageSize] = useState6(25);
2741
- const [filters, setFilters] = useState6({});
3156
+ const [page, setPage] = useState7(0);
3157
+ const [pageSize, setPageSize] = useState7(25);
3158
+ const [filters, setFilters] = useState7({});
2742
3159
  const fetchParams = useCallback2(
2743
3160
  () => ({ page: page + 1, page_size: pageSize, ...filters }),
2744
3161
  [page, pageSize, filters]
@@ -2749,14 +3166,14 @@ var init_AuditLog = __esm({
2749
3166
  );
2750
3167
  const entries = value?.audit_logs ?? [];
2751
3168
  const total = value?.total ?? 0;
2752
- return /* @__PURE__ */ React8.createElement(
3169
+ return /* @__PURE__ */ React9.createElement(
2753
3170
  SectionCard,
2754
3171
  {
2755
3172
  title: "Audit Log",
2756
3173
  subtitle: loading ? "Loading\u2026" : `${total.toLocaleString()} event${total === 1 ? "" : "s"}`,
2757
3174
  flush: true,
2758
- actions: /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
2759
- TextField4,
3175
+ actions: /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
3176
+ TextField5,
2760
3177
  {
2761
3178
  size: "small",
2762
3179
  label: "Action",
@@ -2768,13 +3185,13 @@ var init_AuditLog = __esm({
2768
3185
  },
2769
3186
  sx: { minWidth: 150 }
2770
3187
  },
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,
3188
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All actions"),
3189
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "created" }, "Created"),
3190
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "updated" }, "Updated"),
3191
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "deleted" }, "Deleted"),
3192
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "blocked" }, "Blocked")
3193
+ ), /* @__PURE__ */ React9.createElement(
3194
+ TextField5,
2778
3195
  {
2779
3196
  size: "small",
2780
3197
  label: "Table",
@@ -2786,12 +3203,14 @@ var init_AuditLog = __esm({
2786
3203
  },
2787
3204
  sx: { minWidth: 150 }
2788
3205
  },
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,
3206
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "" }, "All tables"),
3207
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_VerificationToken" }, "Key"),
3208
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamTable" }, "Team"),
3209
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_TeamMembership" }, "Team member"),
3210
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_ObjectPermissionTable" }, "Team access (KB / MCP)"),
3211
+ /* @__PURE__ */ React9.createElement(MenuItem4, { value: "LiteLLM_UserTable" }, "User")
3212
+ ), /* @__PURE__ */ React9.createElement(
3213
+ TextField5,
2795
3214
  {
2796
3215
  size: "small",
2797
3216
  label: "Changed by",
@@ -2804,9 +3223,9 @@ var init_AuditLog = __esm({
2804
3223
  }
2805
3224
  ))
2806
3225
  },
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(
3226
+ error && /* @__PURE__ */ React9.createElement(Box9, { px: 2.5, pb: 2 }, /* @__PURE__ */ React9.createElement(Alert4, { severity: "error" }, error.message)),
3227
+ /* @__PURE__ */ React9.createElement(TableContainer5, null, /* @__PURE__ */ React9.createElement(Table5, { size: "small", sx: dataTableSx }, /* @__PURE__ */ React9.createElement(TableHead5, null, /* @__PURE__ */ React9.createElement(TableRow5, null, /* @__PURE__ */ React9.createElement(TableCell5, { sx: { width: 40 } }), /* @__PURE__ */ React9.createElement(TableCell5, null, "Time"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Action"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Table"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Object ID"), /* @__PURE__ */ React9.createElement(TableCell5, null, "Changed By"))), /* @__PURE__ */ React9.createElement(TableBody5, null, renderAuditLogBody(loading, entries)))),
3228
+ /* @__PURE__ */ React9.createElement(
2810
3229
  TablePagination,
2811
3230
  {
2812
3231
  component: "div",
@@ -2827,22 +3246,53 @@ var init_AuditLog = __esm({
2827
3246
  }
2828
3247
  });
2829
3248
 
3249
+ // src/permissions.ts
3250
+ import { createPermission } from "@backstage/plugin-permission-common";
3251
+ var litellmTeamCreatePermission, litellmTeamManagePermission, litellmTeamMembersManagePermission, litellmTeamKnowledgebaseManagePermission, litellmTeamMcpManagePermission;
3252
+ var init_permissions = __esm({
3253
+ "src/permissions.ts"() {
3254
+ "use strict";
3255
+ litellmTeamCreatePermission = createPermission({
3256
+ name: "litellm.team.create",
3257
+ attributes: { action: "create" }
3258
+ });
3259
+ litellmTeamManagePermission = createPermission({
3260
+ name: "litellm.team.manage",
3261
+ attributes: { action: "update" }
3262
+ });
3263
+ litellmTeamMembersManagePermission = createPermission({
3264
+ name: "litellm.team.members.manage",
3265
+ attributes: { action: "update" }
3266
+ });
3267
+ litellmTeamKnowledgebaseManagePermission = createPermission({
3268
+ name: "litellm.team.knowledgebase.manage",
3269
+ attributes: { action: "update" }
3270
+ });
3271
+ litellmTeamMcpManagePermission = createPermission({
3272
+ name: "litellm.team.mcp.manage",
3273
+ attributes: { action: "update" }
3274
+ });
3275
+ }
3276
+ });
3277
+
2830
3278
  // src/components/LiteLLMPage.tsx
2831
3279
  var LiteLLMPage_exports = {};
2832
3280
  __export(LiteLLMPage_exports, {
2833
3281
  LiteLLMPage: () => LiteLLMPage
2834
3282
  });
2835
- import React9, { useState as useState7, useCallback as useCallback3, useMemo as useMemo6 } from "react";
2836
- import Box9 from "@mui/material/Box";
3283
+ import React10, { useState as useState8, useCallback as useCallback3, useMemo as useMemo6 } from "react";
3284
+ import Box10 from "@mui/material/Box";
2837
3285
  import Snackbar2 from "@mui/material/Snackbar";
2838
- import Alert4 from "@mui/material/Alert";
3286
+ import Alert5 from "@mui/material/Alert";
2839
3287
  import CircularProgress4 from "@mui/material/CircularProgress";
2840
- import Typography9 from "@mui/material/Typography";
3288
+ import Typography10 from "@mui/material/Typography";
2841
3289
  import Paper5 from "@mui/material/Paper";
2842
3290
  import Tabs2 from "@mui/material/Tabs";
2843
3291
  import Tab2 from "@mui/material/Tab";
3292
+ import Button6 from "@mui/material/Button";
2844
3293
  import { useAsync as useAsync2, useAsyncRetry } from "react-use";
2845
3294
  import { useApi } from "@backstage/core-plugin-api";
3295
+ import { usePermission } from "@backstage/plugin-permission-react";
2846
3296
  function initDateRange() {
2847
3297
  let preset = "7d";
2848
3298
  try {
@@ -2864,27 +3314,30 @@ var init_LiteLLMPage = __esm({
2864
3314
  init_DashboardHeader();
2865
3315
  init_KeysTable();
2866
3316
  init_GenerateKeyDialog();
3317
+ init_ManageTeamDialog();
2867
3318
  init_UsageStats();
2868
3319
  init_TeamUsage();
2869
3320
  init_ModelsTable();
2870
3321
  init_AuditLog();
2871
3322
  init_api();
3323
+ init_permissions();
2872
3324
  PERIOD_LS_KEY2 = "litellm_usage_period";
2873
3325
  LiteLLMPage = () => {
2874
3326
  const api = useApi(liteLlmApiRef);
2875
- const [dateRange, setDateRange] = useState7(initDateRange);
2876
- const [currentPreset, setCurrentPreset] = useState7(() => {
3327
+ const [dateRange, setDateRange] = useState8(initDateRange);
3328
+ const [currentPreset, setCurrentPreset] = useState8(() => {
2877
3329
  try {
2878
3330
  return localStorage.getItem(PERIOD_LS_KEY2) ?? "7d";
2879
3331
  } catch {
2880
3332
  return "7d";
2881
3333
  }
2882
3334
  });
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({});
3335
+ const [activeTab, setActiveTab] = useState8("overview");
3336
+ const [snackbar, setSnackbar] = useState8(null);
3337
+ const [generateDialogOpen, setGenerateDialogOpen] = useState8(false);
3338
+ const [manageTeam, setManageTeam] = useState8(null);
3339
+ const [teamUsageCache, setTeamUsageCache] = useState8({});
3340
+ const [teamUsageLoading, setTeamUsageLoading] = useState8({});
2888
3341
  const { value: userInfo, loading: userLoading, error: userError } = useAsync2(
2889
3342
  () => api.getUserInfo(),
2890
3343
  [api]
@@ -2904,11 +3357,30 @@ var init_LiteLLMPage = __esm({
2904
3357
  () => api.listModels().catch(() => []),
2905
3358
  [api]
2906
3359
  );
2907
- const { value: allTeams, loading: teamsLoading } = useAsync2(
3360
+ const { value: allTeams, loading: teamsLoading, retry: refreshTeams } = useAsyncRetry(
2908
3361
  () => api.getTeams().catch(() => []),
2909
3362
  [api]
2910
3363
  );
2911
3364
  const { value: liteLlmConfig } = useAsync2(() => api.getConfig(), [api]);
3365
+ const { value: managedTeams } = useAsync2(
3366
+ async () => liteLlmConfig?.teamManagement?.enabled ? api.getManagedTeams().catch(() => []) : [],
3367
+ [api, liteLlmConfig]
3368
+ );
3369
+ const { allowed: canCreateTeam } = usePermission({ permission: litellmTeamCreatePermission });
3370
+ const { allowed: canManageTeam } = usePermission({ permission: litellmTeamManagePermission });
3371
+ const { allowed: canManageMembers } = usePermission({ permission: litellmTeamMembersManagePermission });
3372
+ const { allowed: canManageKnowledgeBases } = usePermission({ permission: litellmTeamKnowledgebaseManagePermission });
3373
+ const { allowed: canManageMcpServers } = usePermission({ permission: litellmTeamMcpManagePermission });
3374
+ const teamMgmtEnabled = liteLlmConfig?.teamManagement?.enabled ?? false;
3375
+ const objectPermsEnabled = liteLlmConfig?.teamManagement?.objectPermissionsEnabled ?? false;
3376
+ const { value: vectorStores } = useAsync2(
3377
+ async () => objectPermsEnabled ? api.getVectorStores().catch(() => []) : [],
3378
+ [api, objectPermsEnabled]
3379
+ );
3380
+ const { value: mcpServers } = useAsync2(
3381
+ async () => objectPermsEnabled ? api.getMcpServers().catch(() => []) : [],
3382
+ [api, objectPermsEnabled]
3383
+ );
2912
3384
  const teams = useMemo6(() => {
2913
3385
  if (!allTeams?.length) return [];
2914
3386
  if (!userInfo) return allTeams;
@@ -3056,14 +3528,14 @@ var init_LiteLLMPage = __esm({
3056
3528
  );
3057
3529
  const isInitialLoading = userLoading && !userInfo;
3058
3530
  if (isInitialLoading) {
3059
- return /* @__PURE__ */ React9.createElement(Box9, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React9.createElement(CircularProgress4, null));
3531
+ return /* @__PURE__ */ React10.createElement(Box10, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: "50vh" }, /* @__PURE__ */ React10.createElement(CircularProgress4, null));
3060
3532
  }
3061
3533
  if (userError || !userInfo) {
3062
3534
  const isProvisioningEnabled = userError?.body?.provisioning === true;
3063
3535
  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.")));
3536
+ return /* @__PURE__ */ React10.createElement(Box10, { p: 3 }, /* @__PURE__ */ React10.createElement(Paper5, { sx: { p: 3 } }, /* @__PURE__ */ React10.createElement(Typography10, { variant: "h6", gutterBottom: true }, "Account not provisioned"), /* @__PURE__ */ React10.createElement(Typography10, { color: "text.secondary", paragraph: true }, "Your Backstage account is not linked to a LiteLLM user."), hint ? /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, hint) : /* @__PURE__ */ React10.createElement(Typography10, { variant: "body2", color: "text.secondary" }, isProvisioningEnabled ? "Auto-provisioning is enabled but failed. Check the backend logs." : "Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.")));
3065
3537
  }
3066
- const pageTabs = /* @__PURE__ */ React9.createElement(
3538
+ const pageTabs = /* @__PURE__ */ React10.createElement(
3067
3539
  Tabs2,
3068
3540
  {
3069
3541
  value: activeTab,
@@ -3076,13 +3548,13 @@ var init_LiteLLMPage = __esm({
3076
3548
  '& [class*="MuiTab-root"]': { minHeight: 44, textTransform: "none", fontSize: 14 }
3077
3549
  }
3078
3550
  },
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" })
3551
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Overview", value: "overview" }),
3552
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Keys", value: "keys" }),
3553
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Teams", value: "teams" }),
3554
+ /* @__PURE__ */ React10.createElement(Tab2, { label: "Models", value: "models" }),
3555
+ userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(Tab2, { label: "Audit Log", value: "audit" })
3084
3556
  );
3085
- return /* @__PURE__ */ React9.createElement(Box9, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React9.createElement(
3557
+ return /* @__PURE__ */ React10.createElement(Box10, { sx: { p: 3, display: "flex", flexDirection: "column", gap: 2 } }, /* @__PURE__ */ React10.createElement(
3086
3558
  DashboardHeader,
3087
3559
  {
3088
3560
  userInfo,
@@ -3092,7 +3564,7 @@ var init_LiteLLMPage = __esm({
3092
3564
  onGenerateKeyClick: () => setGenerateDialogOpen(true),
3093
3565
  tabs: pageTabs
3094
3566
  }
3095
- ), activeTab === "overview" && /* @__PURE__ */ React9.createElement(
3567
+ ), activeTab === "overview" && /* @__PURE__ */ React10.createElement(
3096
3568
  UsageStats,
3097
3569
  {
3098
3570
  usage: usage ?? null,
@@ -3103,7 +3575,7 @@ var init_LiteLLMPage = __esm({
3103
3575
  loading: usageLoading,
3104
3576
  userInfo
3105
3577
  }
3106
- ), activeTab === "keys" && /* @__PURE__ */ React9.createElement(
3578
+ ), activeTab === "keys" && /* @__PURE__ */ React10.createElement(
3107
3579
  KeysTable,
3108
3580
  {
3109
3581
  keys: keys ?? [],
@@ -3117,25 +3589,40 @@ var init_LiteLLMPage = __esm({
3117
3589
  onDeleteKey: handleDeleteKey,
3118
3590
  onPruneExpiredKeys: handlePruneExpiredKeys
3119
3591
  }
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;
3592
+ ), activeTab === "teams" && (() => {
3593
+ const teamsById = /* @__PURE__ */ new Map();
3594
+ for (const t of managedTeams ?? []) teamsById.set(t.team_id, t);
3595
+ for (const t of teams ?? []) teamsById.set(t.team_id, t);
3596
+ const visibleTeams = Array.from(teamsById.values());
3597
+ return /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", flexDirection: "column", gap: 2 } }, teamMgmtEnabled && canCreateTeam && /* @__PURE__ */ React10.createElement(Box10, { sx: { display: "flex", justifyContent: "flex-end" } }, /* @__PURE__ */ React10.createElement(
3598
+ Button6,
3599
+ {
3600
+ variant: "contained",
3601
+ onClick: () => setManageTeam({ mode: "create" })
3128
3602
  },
3129
- getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false
3130
- }
3131
- ), activeTab === "models" && /* @__PURE__ */ React9.createElement(
3603
+ "Create Team"
3604
+ )), /* @__PURE__ */ React10.createElement(
3605
+ TeamUsage,
3606
+ {
3607
+ teams: visibleTeams,
3608
+ loading: teamsLoading,
3609
+ getTeamUsage: (teamId) => {
3610
+ if (teamUsageCache[teamId] === void 0) loadTeamUsage(teamId);
3611
+ return teamUsageCache[teamId] ?? null;
3612
+ },
3613
+ getTeamUsageLoading: (teamId) => teamUsageLoading[teamId] ?? false,
3614
+ canManage: teamMgmtEnabled && canManageTeam,
3615
+ onEditTeam: (t) => setManageTeam({ mode: "edit", team: t })
3616
+ }
3617
+ ));
3618
+ })(), activeTab === "models" && /* @__PURE__ */ React10.createElement(
3132
3619
  ModelsTable,
3133
3620
  {
3134
3621
  allModels: allModels ?? [],
3135
3622
  teams: teams ?? [],
3136
3623
  loading: modelsLoading
3137
3624
  }
3138
- ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React9.createElement(AuditLog, { api }), /* @__PURE__ */ React9.createElement(
3625
+ ), activeTab === "audit" && userInfo.can_view_audit && /* @__PURE__ */ React10.createElement(AuditLog, { api }), /* @__PURE__ */ React10.createElement(
3139
3626
  GenerateKeyDialog,
3140
3627
  {
3141
3628
  open: generateDialogOpen,
@@ -3148,7 +3635,69 @@ var init_LiteLLMPage = __esm({
3148
3635
  onGenerateKey: handleGenerateKey,
3149
3636
  onGetConfig: () => api.getConfig()
3150
3637
  }
3151
- ), /* @__PURE__ */ React9.createElement(
3638
+ ), /* @__PURE__ */ React10.createElement(
3639
+ ManageTeamDialog,
3640
+ {
3641
+ open: !!manageTeam,
3642
+ onClose: () => setManageTeam(null),
3643
+ mode: manageTeam?.mode ?? "create",
3644
+ team: manageTeam?.team,
3645
+ allModels: allModels ?? [],
3646
+ config: liteLlmConfig,
3647
+ onSubmit: async (payload) => {
3648
+ if (manageTeam?.mode === "edit" && manageTeam.team) {
3649
+ await api.updateTeam(manageTeam.team.team_id, payload);
3650
+ } else {
3651
+ await api.createTeam(payload);
3652
+ }
3653
+ setSnackbar({ message: "Team saved", severity: "success" });
3654
+ refreshTeams();
3655
+ },
3656
+ canManageMembers: teamMgmtEnabled && canManageMembers,
3657
+ onAddMember: async (userEntityRef, maxBudgetInTeam) => {
3658
+ if (!manageTeam?.team) return;
3659
+ const updated = await api.addTeamMember(manageTeam.team.team_id, {
3660
+ userEntityRef,
3661
+ ...maxBudgetInTeam ? { maxBudgetInTeam } : {}
3662
+ });
3663
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3664
+ refreshTeams();
3665
+ },
3666
+ onRemoveMember: async (userEntityRef) => {
3667
+ if (!manageTeam?.team) return;
3668
+ const updated = await api.removeTeamMember(
3669
+ manageTeam.team.team_id,
3670
+ userEntityRef
3671
+ );
3672
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3673
+ refreshTeams();
3674
+ },
3675
+ canManageKnowledgeBases: teamMgmtEnabled && objectPermsEnabled && canManageKnowledgeBases,
3676
+ vectorStores: vectorStores ?? [],
3677
+ onSaveKnowledgeBases: async (vectorStoreIds) => {
3678
+ if (!manageTeam?.team) return;
3679
+ const updated = await api.setTeamKnowledgeBases(
3680
+ manageTeam.team.team_id,
3681
+ vectorStoreIds
3682
+ );
3683
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3684
+ setSnackbar({ message: "Knowledge bases updated", severity: "success" });
3685
+ refreshTeams();
3686
+ },
3687
+ canManageMcpServers: teamMgmtEnabled && objectPermsEnabled && canManageMcpServers,
3688
+ mcpServers: mcpServers ?? [],
3689
+ onSaveMcpServers: async (mcpServerIds) => {
3690
+ if (!manageTeam?.team) return;
3691
+ const updated = await api.setTeamMcpServers(
3692
+ manageTeam.team.team_id,
3693
+ mcpServerIds
3694
+ );
3695
+ setManageTeam((s) => s && s.team ? { ...s, team: updated } : s);
3696
+ setSnackbar({ message: "MCP servers updated", severity: "success" });
3697
+ refreshTeams();
3698
+ }
3699
+ }
3700
+ ), /* @__PURE__ */ React10.createElement(
3152
3701
  Snackbar2,
3153
3702
  {
3154
3703
  open: !!snackbar,
@@ -3156,7 +3705,7 @@ var init_LiteLLMPage = __esm({
3156
3705
  onClose: () => setSnackbar(null),
3157
3706
  anchorOrigin: { vertical: "bottom", horizontal: "right" }
3158
3707
  },
3159
- snackbar ? /* @__PURE__ */ React9.createElement(Alert4, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3708
+ snackbar ? /* @__PURE__ */ React10.createElement(Alert5, { severity: snackbar.severity, onClose: () => setSnackbar(null) }, snackbar.message) : void 0
3160
3709
  ));
3161
3710
  };
3162
3711
  }
@@ -3164,7 +3713,7 @@ var init_LiteLLMPage = __esm({
3164
3713
 
3165
3714
  // src/plugin.tsx
3166
3715
  init_api();
3167
- import React10 from "react";
3716
+ import React11 from "react";
3168
3717
  import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
3169
3718
  import {
3170
3719
  createFrontendPlugin,
@@ -3183,10 +3732,10 @@ var liteLlmPage = PageBlueprint.make({
3183
3732
  params: {
3184
3733
  path: "/litellm",
3185
3734
  title: "LiteLLM",
3186
- icon: /* @__PURE__ */ React10.createElement(TrendingUpIcon, null),
3735
+ icon: /* @__PURE__ */ React11.createElement(TrendingUpIcon, null),
3187
3736
  loader: async () => {
3188
3737
  const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
3189
- return /* @__PURE__ */ React10.createElement(LiteLLMPage2, null);
3738
+ return /* @__PURE__ */ React11.createElement(LiteLLMPage2, null);
3190
3739
  }
3191
3740
  }
3192
3741
  });
@@ -3205,16 +3754,16 @@ init_TeamUsage();
3205
3754
  // src/components/LiteLLMHomeWidget.tsx
3206
3755
  init_api();
3207
3756
  init_format();
3208
- import React11, { useState as useState8, useEffect as useEffect2 } from "react";
3757
+ import React12, { useState as useState9, useEffect as useEffect3 } from "react";
3209
3758
  import Paper6 from "@mui/material/Paper";
3210
- import Box10 from "@mui/material/Box";
3211
- import Typography10 from "@mui/material/Typography";
3759
+ import Box11 from "@mui/material/Box";
3760
+ import Typography11 from "@mui/material/Typography";
3212
3761
  import FormControl2 from "@mui/material/FormControl";
3213
3762
  import Select2 from "@mui/material/Select";
3214
3763
  import MenuItem5 from "@mui/material/MenuItem";
3215
3764
  import Grid2 from "@mui/material/Grid";
3216
3765
  import CircularProgress5 from "@mui/material/CircularProgress";
3217
- import Alert5 from "@mui/material/Alert";
3766
+ import Alert6 from "@mui/material/Alert";
3218
3767
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
3219
3768
  import { useApi as useApi2 } from "@backstage/core-plugin-api";
3220
3769
  function presetToDateRange(preset) {
@@ -3229,19 +3778,19 @@ function presetToDateRange(preset) {
3229
3778
  }
3230
3779
  return { start, end };
3231
3780
  }
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));
3781
+ 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
3782
  var LiteLLMHomeWidget = ({
3234
3783
  defaultPeriod = "7d",
3235
3784
  title = "LiteLLM Usage"
3236
3785
  }) => {
3237
3786
  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(() => {
3787
+ const [period, setPeriod] = useState9(defaultPeriod);
3788
+ const [loading, setLoading] = useState9(true);
3789
+ const [usageError, setUsageError] = useState9(null);
3790
+ const [keysError, setKeysError] = useState9(null);
3791
+ const [usage, setUsage] = useState9(null);
3792
+ const [keys, setKeys] = useState9([]);
3793
+ useEffect3(() => {
3245
3794
  let cancelled = false;
3246
3795
  setLoading(true);
3247
3796
  setUsageError(null);
@@ -3278,17 +3827,17 @@ var LiteLLMHomeWidget = ({
3278
3827
  spend: d.spend
3279
3828
  }));
3280
3829
  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(
3830
+ return /* @__PURE__ */ React12.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React12.createElement(Typography11, { variant: "h6" }, title), /* @__PURE__ */ React12.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React12.createElement(
3282
3831
  Select2,
3283
3832
  {
3284
3833
  value: period,
3285
3834
  onChange: (e) => setPeriod(e.target.value),
3286
3835
  displayEmpty: true
3287
3836
  },
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(
3837
+ /* @__PURE__ */ React12.createElement(MenuItem5, { value: "today" }, "Today"),
3838
+ /* @__PURE__ */ React12.createElement(MenuItem5, { value: "7d" }, "7d"),
3839
+ /* @__PURE__ */ React12.createElement(MenuItem5, { value: "30d" }, "30d")
3840
+ ))), loading && /* @__PURE__ */ React12.createElement(Box11, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React12.createElement(CircularProgress5, { size: 32 })), !loading && totalFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "error", sx: { mt: 1 } }, usageError ?? "Failed to load usage data"), !loading && !totalFailure && /* @__PURE__ */ React12.createElement(React12.Fragment, null, partialFailure && /* @__PURE__ */ React12.createElement(Alert6, { severity: "warning", sx: { mt: 1, mb: 1 } }, usageError ? `Usage data unavailable (${usageError}).` : "", keysError ? ` Key list unavailable (${keysError}).` : "", " Showing what loaded."), /* @__PURE__ */ React12.createElement(Grid2, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "USD Spent", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens In", value: fmtInt(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Tokens Out", value: fmtInt(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React12.createElement(Grid2, { item: true, xs: 6 }, /* @__PURE__ */ React12.createElement(Kpi, { label: "Keys", value: fmtInt(keys.length) }))), hasSparkline && /* @__PURE__ */ React12.createElement(Box11, { height: 120 }, /* @__PURE__ */ React12.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React12.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React12.createElement(
3292
3841
  Area3,
3293
3842
  {
3294
3843
  type: "monotone",
@@ -3304,7 +3853,9 @@ var LiteLLMHomeWidget = ({
3304
3853
 
3305
3854
  // src/index.ts
3306
3855
  init_GenerateKeyDialog();
3856
+ init_ManageTeamDialog();
3307
3857
  init_api();
3858
+ init_permissions();
3308
3859
  export {
3309
3860
  DashboardHeader,
3310
3861
  GenerateKeyDialog,
@@ -3312,9 +3863,15 @@ export {
3312
3863
  LiteLLMHomeWidget,
3313
3864
  LiteLLMPage,
3314
3865
  LiteLlmApi,
3866
+ ManageTeamDialog,
3315
3867
  TeamUsage,
3316
3868
  UsageStats,
3317
3869
  liteLlmApiRef,
3318
- litellmPlugin
3870
+ litellmPlugin,
3871
+ litellmTeamCreatePermission,
3872
+ litellmTeamKnowledgebaseManagePermission,
3873
+ litellmTeamManagePermission,
3874
+ litellmTeamMcpManagePermission,
3875
+ litellmTeamMembersManagePermission
3319
3876
  };
3320
3877
  //# sourceMappingURL=index.esm.js.map