@acarmisc/backstage-plugin-litellm 0.1.8 → 0.1.10
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 +116 -5
- package/dist/index.esm.js.map +2 -2
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -77,6 +77,9 @@ var init_api = __esm({
|
|
|
77
77
|
async generateKey(request) {
|
|
78
78
|
return this.post("/keys/generate", request);
|
|
79
79
|
}
|
|
80
|
+
async updateKey(keyId, request) {
|
|
81
|
+
return this.post(`/keys/${encodeURIComponent(keyId)}/update`, request);
|
|
82
|
+
}
|
|
80
83
|
async deleteKey(keyId) {
|
|
81
84
|
return this.del(`/keys/${encodeURIComponent(keyId)}`);
|
|
82
85
|
}
|
|
@@ -163,11 +166,16 @@ import {
|
|
|
163
166
|
CircularProgress,
|
|
164
167
|
Autocomplete
|
|
165
168
|
} from "@mui/material";
|
|
166
|
-
import { ContentCopy, Delete, Add, Visibility, VisibilityOff } from "@mui/icons-material";
|
|
167
|
-
var maskKey, formatDate, emptyForm, KeysTable;
|
|
169
|
+
import { ContentCopy, Delete, Add, Edit, Visibility, VisibilityOff } from "@mui/icons-material";
|
|
170
|
+
var KEY_TYPES, maskKey, formatDate, emptyForm, keyToEditForm, KeysTable;
|
|
168
171
|
var init_KeysTable = __esm({
|
|
169
172
|
"src/components/KeysTable.tsx"() {
|
|
170
173
|
"use strict";
|
|
174
|
+
KEY_TYPES = [
|
|
175
|
+
{ value: "llm_api", label: "AI API" },
|
|
176
|
+
{ value: "management", label: "Management" },
|
|
177
|
+
{ value: "read_only", label: "Read Only" }
|
|
178
|
+
];
|
|
171
179
|
maskKey = (key) => {
|
|
172
180
|
if (key.length <= 8) return "***";
|
|
173
181
|
return `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
@@ -185,7 +193,15 @@ var init_KeysTable = __esm({
|
|
|
185
193
|
duration: "30d",
|
|
186
194
|
max_budget: void 0,
|
|
187
195
|
tpm_limit: void 0,
|
|
188
|
-
team_id: void 0
|
|
196
|
+
team_id: void 0,
|
|
197
|
+
key_type: "llm_api"
|
|
198
|
+
});
|
|
199
|
+
keyToEditForm = (k) => ({
|
|
200
|
+
key_alias: k.key_alias ?? "",
|
|
201
|
+
models: k.models ?? [],
|
|
202
|
+
max_budget: k.max_budget,
|
|
203
|
+
tpm_limit: k.tpm_limit,
|
|
204
|
+
rpm_limit: k.rpm_limit
|
|
189
205
|
});
|
|
190
206
|
KeysTable = ({
|
|
191
207
|
keys,
|
|
@@ -193,6 +209,7 @@ var init_KeysTable = __esm({
|
|
|
193
209
|
teams,
|
|
194
210
|
loading,
|
|
195
211
|
onGenerateKey,
|
|
212
|
+
onUpdateKey,
|
|
196
213
|
onDeleteKey
|
|
197
214
|
}) => {
|
|
198
215
|
const [generateModalOpen, setGenerateModalOpen] = useState(false);
|
|
@@ -200,8 +217,12 @@ var init_KeysTable = __esm({
|
|
|
200
217
|
const [newKeyValue, setNewKeyValue] = useState(null);
|
|
201
218
|
const [formData, setFormData] = useState(emptyForm());
|
|
202
219
|
const [submitting, setSubmitting] = useState(false);
|
|
220
|
+
const [editingKey, setEditingKey] = useState(null);
|
|
221
|
+
const [editForm, setEditForm] = useState({});
|
|
222
|
+
const [editSubmitting, setEditSubmitting] = useState(false);
|
|
203
223
|
const selectedModels = models.filter((m) => (formData.models || []).includes(m.model_name));
|
|
204
224
|
const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
|
|
225
|
+
const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
|
|
205
226
|
const handleGenerate = async () => {
|
|
206
227
|
setSubmitting(true);
|
|
207
228
|
try {
|
|
@@ -219,6 +240,26 @@ var init_KeysTable = __esm({
|
|
|
219
240
|
setNewKeyValue(null);
|
|
220
241
|
setFormData(emptyForm());
|
|
221
242
|
};
|
|
243
|
+
const handleOpenEdit = (k) => {
|
|
244
|
+
setEditingKey(k);
|
|
245
|
+
setEditForm(keyToEditForm(k));
|
|
246
|
+
};
|
|
247
|
+
const handleCloseEdit = () => {
|
|
248
|
+
setEditingKey(null);
|
|
249
|
+
setEditForm({});
|
|
250
|
+
};
|
|
251
|
+
const handleUpdate = async () => {
|
|
252
|
+
if (!editingKey) return;
|
|
253
|
+
setEditSubmitting(true);
|
|
254
|
+
try {
|
|
255
|
+
await onUpdateKey(editingKey.key, editForm);
|
|
256
|
+
handleCloseEdit();
|
|
257
|
+
} catch (error) {
|
|
258
|
+
console.error("Failed to update key:", error);
|
|
259
|
+
} finally {
|
|
260
|
+
setEditSubmitting(false);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
222
263
|
const copyToClipboard = (text) => {
|
|
223
264
|
navigator.clipboard.writeText(text);
|
|
224
265
|
};
|
|
@@ -238,7 +279,7 @@ var init_KeysTable = __esm({
|
|
|
238
279
|
onClick: () => setShowKeyValue(showKeyValue === key.key ? null : key.key)
|
|
239
280
|
},
|
|
240
281
|
showKeyValue === key.key ? /* @__PURE__ */ React2.createElement(VisibilityOff, null) : /* @__PURE__ */ React2.createElement(Visibility, null)
|
|
241
|
-
), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(key.key) }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, "$", key.spend?.toFixed(4) || "0.00"), /* @__PURE__ */ React2.createElement(TableCell, null, key.max_budget ? `$${key.max_budget}` : "-"), /* @__PURE__ */ React2.createElement(TableCell, null, key.tpm_limit || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { color: "error", onClick: () => onDeleteKey(key.key) }, /* @__PURE__ */ React2.createElement(Delete, null))))))))), /* @__PURE__ */ React2.createElement(Dialog, { open: generateModalOpen, onClose: handleCloseModal, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, newKeyValue ? /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React2.createElement(
|
|
282
|
+
), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(key.key) }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, "$", key.spend?.toFixed(4) || "0.00"), /* @__PURE__ */ React2.createElement(TableCell, null, key.max_budget ? `$${key.max_budget}` : "-"), /* @__PURE__ */ React2.createElement(TableCell, null, key.tpm_limit || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { onClick: () => handleOpenEdit(key) }, /* @__PURE__ */ React2.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React2.createElement(IconButton, { color: "error", onClick: () => onDeleteKey(key.key) }, /* @__PURE__ */ React2.createElement(Delete, null))))))))), /* @__PURE__ */ React2.createElement(Dialog, { open: generateModalOpen, onClose: handleCloseModal, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, newKeyValue ? /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React2.createElement(
|
|
242
283
|
Box2,
|
|
243
284
|
{
|
|
244
285
|
display: "flex",
|
|
@@ -265,6 +306,16 @@ var init_KeysTable = __esm({
|
|
|
265
306
|
onChange: (e) => setFormData({ ...formData, alias: e.target.value }),
|
|
266
307
|
fullWidth: true
|
|
267
308
|
}
|
|
309
|
+
), /* @__PURE__ */ React2.createElement(
|
|
310
|
+
TextField,
|
|
311
|
+
{
|
|
312
|
+
select: true,
|
|
313
|
+
label: "Key Type",
|
|
314
|
+
value: formData.key_type || "llm_api",
|
|
315
|
+
onChange: (e) => setFormData({ ...formData, key_type: e.target.value }),
|
|
316
|
+
fullWidth: true
|
|
317
|
+
},
|
|
318
|
+
KEY_TYPES.map((kt) => /* @__PURE__ */ React2.createElement(MenuItem, { key: kt.value, value: kt.value }, kt.label))
|
|
268
319
|
), /* @__PURE__ */ React2.createElement(
|
|
269
320
|
TextField,
|
|
270
321
|
{
|
|
@@ -318,7 +369,53 @@ var init_KeysTable = __esm({
|
|
|
318
369
|
onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
319
370
|
fullWidth: true
|
|
320
371
|
}
|
|
321
|
-
))), /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal }, newKeyValue ? "Done" : "Cancel"), !newKeyValue && /* @__PURE__ */ React2.createElement(Button, { onClick: handleGenerate, variant: "contained", color: "primary", disabled: submitting }, submitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Generate"))))
|
|
372
|
+
))), /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal }, newKeyValue ? "Done" : "Cancel"), !newKeyValue && /* @__PURE__ */ React2.createElement(Button, { onClick: handleGenerate, variant: "contained", color: "primary", disabled: submitting }, submitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Generate"))), /* @__PURE__ */ React2.createElement(Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, "Edit Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, editingKey && /* @__PURE__ */ React2.createElement(Box2, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React2.createElement("code", { style: { fontFamily: "monospace" } }, maskKey(editingKey.key))), /* @__PURE__ */ React2.createElement(
|
|
373
|
+
TextField,
|
|
374
|
+
{
|
|
375
|
+
label: "Alias",
|
|
376
|
+
value: editForm.key_alias || "",
|
|
377
|
+
onChange: (e) => setEditForm({ ...editForm, key_alias: e.target.value }),
|
|
378
|
+
fullWidth: true
|
|
379
|
+
}
|
|
380
|
+
), models.length > 0 && /* @__PURE__ */ React2.createElement(
|
|
381
|
+
Autocomplete,
|
|
382
|
+
{
|
|
383
|
+
multiple: true,
|
|
384
|
+
options: models,
|
|
385
|
+
groupBy: (m) => m.mode || "other",
|
|
386
|
+
getOptionLabel: (m) => m.model_name,
|
|
387
|
+
value: editSelectedModels,
|
|
388
|
+
onChange: (_e, selected) => setEditForm({ ...editForm, models: selected.map((m) => m.model_name) }),
|
|
389
|
+
renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Models", fullWidth: true })
|
|
390
|
+
}
|
|
391
|
+
), /* @__PURE__ */ React2.createElement(
|
|
392
|
+
TextField,
|
|
393
|
+
{
|
|
394
|
+
label: "Max Budget (USD)",
|
|
395
|
+
type: "number",
|
|
396
|
+
value: editForm.max_budget ?? "",
|
|
397
|
+
onChange: (e) => setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
|
|
398
|
+
fullWidth: true
|
|
399
|
+
}
|
|
400
|
+
), /* @__PURE__ */ React2.createElement(
|
|
401
|
+
TextField,
|
|
402
|
+
{
|
|
403
|
+
label: "TPM Limit",
|
|
404
|
+
type: "number",
|
|
405
|
+
value: editForm.tpm_limit ?? "",
|
|
406
|
+
onChange: (e) => setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
407
|
+
fullWidth: true
|
|
408
|
+
}
|
|
409
|
+
), /* @__PURE__ */ React2.createElement(
|
|
410
|
+
TextField,
|
|
411
|
+
{
|
|
412
|
+
label: "RPM Limit",
|
|
413
|
+
type: "number",
|
|
414
|
+
value: editForm.rpm_limit ?? "",
|
|
415
|
+
onChange: (e) => setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
|
|
416
|
+
fullWidth: true
|
|
417
|
+
}
|
|
418
|
+
))), /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseEdit }, "Cancel"), /* @__PURE__ */ React2.createElement(Button, { onClick: handleUpdate, variant: "contained", color: "primary", disabled: editSubmitting }, editSubmitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Save"))));
|
|
322
419
|
};
|
|
323
420
|
}
|
|
324
421
|
});
|
|
@@ -598,6 +695,19 @@ var init_LiteLLMPage = __esm({
|
|
|
598
695
|
},
|
|
599
696
|
[api, refreshKeys]
|
|
600
697
|
);
|
|
698
|
+
const handleUpdateKey = useCallback(
|
|
699
|
+
async (keyId, request) => {
|
|
700
|
+
try {
|
|
701
|
+
await api.updateKey(keyId, request);
|
|
702
|
+
setSnackbar({ message: "Key updated successfully", severity: "success" });
|
|
703
|
+
refreshKeys();
|
|
704
|
+
} catch (e) {
|
|
705
|
+
setSnackbar({ message: `Failed to update key: ${e.message}`, severity: "error" });
|
|
706
|
+
throw e;
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
[api, refreshKeys]
|
|
710
|
+
);
|
|
601
711
|
const handleDeleteKey = useCallback(
|
|
602
712
|
async (keyId) => {
|
|
603
713
|
try {
|
|
@@ -639,6 +749,7 @@ var init_LiteLLMPage = __esm({
|
|
|
639
749
|
teams: teams ?? [],
|
|
640
750
|
loading: keysLoading || modelsLoading,
|
|
641
751
|
onGenerateKey: handleGenerateKey,
|
|
752
|
+
onUpdateKey: handleUpdateKey,
|
|
642
753
|
onDeleteKey: handleDeleteKey
|
|
643
754
|
}
|
|
644
755
|
)), /* @__PURE__ */ React5.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/api.ts", "../src/components/DashboardHeader.tsx", "../src/components/KeysTable.tsx", "../src/components/UsageStats.tsx", "../src/components/TeamUsage.tsx", "../src/components/LiteLLMPage.tsx", "../src/plugin.ts", "../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["import { createApiRef, FetchApi } from '@backstage/core-plugin-api';\nimport {\n UserInfo,\n VirtualKey,\n ModelInfo,\n UsageMetrics,\n TeamInfo,\n GenerateKeyRequest,\n GenerateKeyResponse,\n} from './types';\n\nclass ApiError extends Error {\n body: unknown;\n status: number;\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface LiteLlmApiInterface {\n getUserInfo(): Promise<UserInfo>;\n listKeys(): Promise<VirtualKey[]>;\n generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse>;\n deleteKey(keyId: string): Promise<{ success: boolean }>;\n listModels(): Promise<ModelInfo[]>;\n getTeams(): Promise<TeamInfo[]>;\n getUsage(startDate: string, endDate: string): Promise<UsageMetrics>;\n getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics>;\n}\n\nexport const liteLlmApiRef = createApiRef<LiteLlmApiInterface>({\n id: 'plugin.litellm.api',\n});\n\nexport class LiteLlmApi implements LiteLlmApiInterface {\n private fetchApi: FetchApi;\n private basePath: string;\n\n constructor(fetchApi: FetchApi, basePath: string = '/api/litellm') {\n this.fetchApi = fetchApi;\n this.basePath = basePath;\n }\n\n private async throwIfNotOk(response: Response): Promise<void> {\n if (!response.ok) {\n let body: unknown;\n try { body = await response.json(); } catch { body = await response.text().catch(() => ''); }\n throw new ApiError(`${response.status} ${response.statusText}`, response.status, body);\n }\n }\n\n private async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.basePath}${path}`, window.location.origin);\n if (params) {\n Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value));\n }\n const response = await this.fetchApi.fetch(url.toString());\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async del<T>(path: string): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n // User identity is resolved server-side from the Backstage Bearer token.\n // No user_id param needed on the frontend.\n async getUserInfo(): Promise<UserInfo> {\n return this.get<UserInfo>('/user/info');\n }\n\n async listKeys(): Promise<VirtualKey[]> {\n return this.get<VirtualKey[]>('/keys');\n }\n\n async generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse> {\n return this.post<GenerateKeyResponse>('/keys/generate', request);\n }\n\n async deleteKey(keyId: string): Promise<{ success: boolean }> {\n return this.del<{ success: boolean }>(`/keys/${encodeURIComponent(keyId)}`);\n }\n\n async listModels(): Promise<ModelInfo[]> {\n return this.get<ModelInfo[]>('/models');\n }\n\n async getTeams(): Promise<TeamInfo[]> {\n return this.get<TeamInfo[]>('/teams');\n }\n\n async getUsage(startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>('/usage', { start_date: startDate, end_date: endDate });\n }\n\n async getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>(`/teams/${encodeURIComponent(teamId)}/usage`, {\n start_date: startDate,\n end_date: endDate,\n });\n }\n}\n", "import React from 'react';\nimport { Box, Typography, LinearProgress, Paper, Chip } from '@mui/material';\nimport { Warning } from '@mui/icons-material';\nimport { UserInfo, TeamInfo } from '../types';\n\ninterface DashboardHeaderProps {\n userInfo: UserInfo;\n teams: TeamInfo[];\n loading: boolean;\n}\n\nexport const DashboardHeader: React.FC<DashboardHeaderProps> = ({ userInfo, teams, loading }) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;\n const budget = userInfo.max_budget ?? 0;\n const spend = userInfo.spend ?? userInfo.current_spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"flex-start\" gap={2} flexWrap=\"wrap\">\n <Box flexGrow={1}>\n <Typography variant=\"h6\">{displayName}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {userInfo.user_id}\n </Typography>\n\n {teams.length > 0 && (\n <Box display=\"flex\" gap={0.75} flexWrap=\"wrap\" mt={1}>\n {teams.map(team => (\n <Chip\n key={team.team_id}\n label={team.team_alias || team.team_id}\n size=\"small\"\n variant=\"outlined\"\n color=\"primary\"\n />\n ))}\n </Box>\n )}\n </Box>\n\n {budget > 0 && (\n <Box minWidth={220}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip icon={<Warning />} label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 8, borderRadius: 1 }}\n />\n </Box>\n )}\n </Box>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Button,\n IconButton,\n Box,\n Typography,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n MenuItem,\n Chip,\n CircularProgress,\n Autocomplete,\n} from '@mui/material';\nimport { ContentCopy, Delete, Add, Visibility, VisibilityOff } from '@mui/icons-material';\nimport { VirtualKey, ModelInfo, TeamInfo, GenerateKeyRequest, GenerateKeyResponse } from '../types';\n\ninterface KeysTableProps {\n keys: VirtualKey[];\n models: ModelInfo[];\n teams: TeamInfo[];\n loading: boolean;\n onGenerateKey: (request: GenerateKeyRequest) => Promise<GenerateKeyResponse>;\n onDeleteKey: (keyId: string) => Promise<void>;\n}\n\nconst maskKey = (key: string): string => {\n if (key.length <= 8) return '***';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n};\n\nconst formatDate = (dateStr: string): string => {\n try {\n return new Date(dateStr).toLocaleDateString();\n } catch {\n return dateStr;\n }\n};\n\nconst emptyForm = (): GenerateKeyRequest => ({\n alias: '',\n models: [],\n duration: '30d',\n max_budget: undefined,\n tpm_limit: undefined,\n team_id: undefined,\n});\n\nexport const KeysTable: React.FC<KeysTableProps> = ({\n keys,\n models,\n teams,\n loading,\n onGenerateKey,\n onDeleteKey,\n}) => {\n const [generateModalOpen, setGenerateModalOpen] = useState(false);\n const [showKeyValue, setShowKeyValue] = useState<string | null>(null);\n const [newKeyValue, setNewKeyValue] = useState<string | null>(null);\n const [formData, setFormData] = useState<GenerateKeyRequest>(emptyForm());\n const [submitting, setSubmitting] = useState(false);\n\n const selectedModels = models.filter(m => (formData.models || []).includes(m.model_name));\n const selectedTeam = teams.find(t => t.team_id === formData.team_id) ?? null;\n\n const handleGenerate = async () => {\n setSubmitting(true);\n try {\n const response = await onGenerateKey(formData);\n setNewKeyValue(response.key);\n setFormData(emptyForm());\n } catch (error) {\n console.error('Failed to generate key:', error);\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleCloseModal = () => {\n setGenerateModalOpen(false);\n setNewKeyValue(null);\n setFormData(emptyForm());\n };\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text);\n };\n\n return (\n <>\n <Paper sx={{ mb: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" p={2}>\n <Typography variant=\"h6\">Virtual Keys</Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<Add />}\n onClick={() => setGenerateModalOpen(true)}\n >\n Generate New Key\n </Button>\n </Box>\n\n <TableContainer>\n <Table>\n <TableHead>\n <TableRow>\n <TableCell>Alias</TableCell>\n <TableCell>Key</TableCell>\n <TableCell>Created</TableCell>\n <TableCell>Spend</TableCell>\n <TableCell>Budget</TableCell>\n <TableCell>TPM Limit</TableCell>\n <TableCell>Models</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <CircularProgress size={24} />\n </TableCell>\n </TableRow>\n ) : keys.length === 0 ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <Typography color=\"text.secondary\">No keys found</Typography>\n </TableCell>\n </TableRow>\n ) : (\n keys.map((key) => (\n <TableRow key={key.key}>\n <TableCell>{key.key_alias || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" alignItems=\"center\" gap={0.5}>\n <Typography variant=\"body2\" component=\"code\" sx={{ fontFamily: 'monospace' }}>\n {showKeyValue === key.key ? key.key : maskKey(key.key)}\n </Typography>\n <IconButton\n size=\"small\"\n onClick={() => setShowKeyValue(showKeyValue === key.key ? null : key.key)}\n >\n {showKeyValue === key.key ? <VisibilityOff /> : <Visibility />}\n </IconButton>\n <IconButton size=\"small\" onClick={() => copyToClipboard(key.key)}>\n <ContentCopy fontSize=\"small\" />\n </IconButton>\n </Box>\n </TableCell>\n <TableCell>{formatDate(key.created_at)}</TableCell>\n <TableCell>${key.spend?.toFixed(4) || '0.00'}</TableCell>\n <TableCell>{key.max_budget ? `$${key.max_budget}` : '-'}</TableCell>\n <TableCell>{key.tpm_limit || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\">\n {key.models?.slice(0, 2).map((model) => (\n <Chip key={model} label={model} size=\"small\" />\n ))}\n {(key.models?.length || 0) > 2 && (\n <Chip label={`+${(key.models?.length || 0) - 2}`} size=\"small\" variant=\"outlined\" />\n )}\n </Box>\n </TableCell>\n <TableCell align=\"right\">\n <IconButton color=\"error\" onClick={() => onDeleteKey(key.key)}>\n <Delete />\n </IconButton>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </TableContainer>\n </Paper>\n\n <Dialog open={generateModalOpen} onClose={handleCloseModal} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{newKeyValue ? 'Key Generated' : 'Generate New Key'}</DialogTitle>\n <DialogContent>\n {newKeyValue ? (\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n Copy this key now. You won't be able to see it again.\n </Typography>\n <Box\n display=\"flex\"\n alignItems=\"center\"\n gap={1}\n mt={2}\n p={2}\n sx={{ backgroundColor: 'action.hover', borderRadius: 1 }}\n >\n <Typography\n component=\"code\"\n sx={{ fontFamily: 'monospace', wordBreak: 'break-all', flex: 1 }}\n >\n {newKeyValue}\n </Typography>\n <IconButton onClick={() => copyToClipboard(newKeyValue)}>\n <ContentCopy />\n </IconButton>\n </Box>\n </Box>\n ) : (\n <Box display=\"flex\" flexDirection=\"column\" gap={2} mt={1}>\n <TextField\n label=\"Alias\"\n value={formData.alias || ''}\n onChange={(e) => setFormData({ ...formData, alias: e.target.value })}\n fullWidth\n />\n <TextField\n select\n label=\"Duration\"\n value={formData.duration || '30d'}\n onChange={(e) => setFormData({ ...formData, duration: e.target.value })}\n fullWidth\n >\n <MenuItem value=\"1d\">1 Day</MenuItem>\n <MenuItem value=\"7d\">7 Days</MenuItem>\n <MenuItem value=\"30d\">30 Days</MenuItem>\n <MenuItem value=\"90d\">90 Days</MenuItem>\n <MenuItem value=\"1y\">1 Year</MenuItem>\n </TextField>\n\n {teams.length > 0 && (\n <Autocomplete\n options={teams}\n getOptionLabel={t => t.team_alias || t.team_id}\n value={selectedTeam}\n onChange={(_e, team) =>\n setFormData({ ...formData, team_id: team?.team_id })\n }\n renderInput={params => (\n <TextField {...params} label=\"Team (optional)\" fullWidth />\n )}\n />\n )}\n\n {models.length > 0 && (\n <Autocomplete\n multiple\n options={models}\n groupBy={m => m.mode || 'other'}\n getOptionLabel={m => m.model_name}\n value={selectedModels}\n onChange={(_e, selected) =>\n setFormData({ ...formData, models: selected.map(m => m.model_name) })\n }\n renderOption={(props, m) => (\n <li {...props}>\n {m.model_name}\n {m.supports_function_calling && ' \uD83D\uDD27'}\n {m.supports_vision && ' \uD83D\uDC41\uFE0F'}\n </li>\n )}\n renderInput={params => (\n <TextField {...params} label=\"Models\" fullWidth />\n )}\n />\n )}\n\n <TextField\n label=\"Max Budget (USD)\"\n type=\"number\"\n value={formData.max_budget ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"TPM Limit\"\n type=\"number\"\n value={formData.tpm_limit ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseModal}>{newKeyValue ? 'Done' : 'Cancel'}</Button>\n {!newKeyValue && (\n <Button onClick={handleGenerate} variant=\"contained\" color=\"primary\" disabled={submitting}>\n {submitting ? <CircularProgress size={24} /> : 'Generate'}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n </>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n CircularProgress,\n} from '@mui/material';\nimport {\n AreaChart,\n Area,\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { DateRange, UsageMetrics, ModelInfo } from '../types';\n\ninterface UsageStatsProps {\n usage: UsageMetrics | null;\n models: ModelInfo[];\n dateRange: DateRange;\n onDateRangeChange: (range: DateRange) => void;\n loading: boolean;\n}\n\ntype DatePreset = 'today' | '7d' | '30d';\n\nexport const UsageStats: React.FC<UsageStatsProps> = ({\n usage,\n models,\n dateRange,\n onDateRangeChange,\n loading,\n}) => {\n const [selectedPreset, setSelectedPreset] = useState<DatePreset>('7d');\n const [selectedModel, setSelectedModel] = useState<string>('all');\n\n const handlePresetChange = (preset: DatePreset) => {\n setSelectedPreset(preset);\n const end = new Date();\n const start = new Date();\n if (preset === 'today') start.setHours(0, 0, 0, 0);\n else if (preset === '7d') start.setDate(start.getDate() - 7);\n else if (preset === '30d') start.setDate(start.getDate() - 30);\n onDateRangeChange({ start, end });\n };\n\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"center\" p={4}>\n <CircularProgress />\n </Box>\n </Paper>\n );\n }\n\n const dailyData =\n usage?.daily_usage?.map((d) => ({\n date: d.date,\n spend: d.spend,\n promptTokens: d.prompt_tokens,\n completionTokens: d.completion_tokens,\n totalTokens: d.total_tokens,\n })) || [];\n\n const usageByModelData = Object.entries(usage?.usage_by_model || {}).map(([model, data]) => ({\n model,\n spend: data.total_spend,\n promptTokens: data.prompt_tokens,\n completionTokens: data.completion_tokens,\n }));\n\n const filteredModelData =\n selectedModel === 'all'\n ? usageByModelData\n : usageByModelData.filter((d) => d.model === selectedModel);\n\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={3}>\n <Typography variant=\"h6\">Usage Analytics</Typography>\n <Box display=\"flex\" gap={2} alignItems=\"center\">\n <FormControl size=\"small\" sx={{ minWidth: 140 }}>\n <InputLabel>Period</InputLabel>\n <Select\n value={selectedPreset}\n label=\"Period\"\n onChange={(e) => handlePresetChange(e.target.value as DatePreset)}\n >\n <MenuItem value=\"today\">Today</MenuItem>\n <MenuItem value=\"7d\">Last 7 days</MenuItem>\n <MenuItem value=\"30d\">Last 30 days</MenuItem>\n </Select>\n </FormControl>\n <FormControl size=\"small\" sx={{ minWidth: 150 }}>\n <InputLabel>Model</InputLabel>\n <Select\n value={selectedModel}\n label=\"Model\"\n onChange={(e) => setSelectedModel(e.target.value)}\n >\n <MenuItem value=\"all\">All Models</MenuItem>\n {models.map((m) => (\n <MenuItem key={m.model_name} value={m.model_name}>\n {m.model_name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Box>\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 12 }} />\n <YAxis tick={{ fontSize: 12 }} tickFormatter={(v) => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(value: number) => [`$${value.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Token Usage by Model\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={filteredModelData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"model\" tick={{ fontSize: 10 }} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"promptTokens\" name=\"Prompt Tokens\" fill=\"#8884d8\" stackId=\"a\" />\n <Bar dataKey=\"completionTokens\" name=\"Completion Tokens\" fill=\"#82ca9d\" stackId=\"a\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12}>\n <Box display=\"flex\" gap={4} flexWrap=\"wrap\">\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Spend</Typography>\n <Typography variant=\"h5\">${usage?.total_spend?.toFixed(4) || '0.00'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Tokens</Typography>\n <Typography variant=\"h5\">{usage?.total_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Prompt Tokens</Typography>\n <Typography variant=\"h5\">{usage?.prompt_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Completion Tokens</Typography>\n <Typography variant=\"h5\">{usage?.completion_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n </Box>\n </Grid>\n </Grid>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n LinearProgress,\n Chip,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TableContainer,\n Collapse,\n IconButton,\n CircularProgress,\n} from '@mui/material';\nimport { ExpandMore, ExpandLess, Group } from '@mui/icons-material';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n} from 'recharts';\nimport { TeamInfo, UsageMetrics, DateRange } from '../types';\n\ninterface TeamCardProps {\n team: TeamInfo;\n usage: UsageMetrics | null;\n usageLoading: boolean;\n}\n\nconst TeamCard: React.FC<TeamCardProps> = ({ team, usage, usageLoading }) => {\n const [expanded, setExpanded] = useState(false);\n\n const budget = team.max_budget ?? 0;\n const spend = team.spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n const dailyData = usage?.daily_usage?.map(d => ({ date: d.date, spend: d.spend })) ?? [];\n\n return (\n <Paper variant=\"outlined\" sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"action\" />\n <Box flexGrow={1}>\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {team.team_alias ?? team.team_id}\n </Typography>\n {team.models?.length ? (\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\" mt={0.5}>\n {team.models.map(m => (\n <Chip key={m} label={m} size=\"small\" variant=\"outlined\" />\n ))}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">All models</Typography>\n )}\n </Box>\n <IconButton size=\"small\" onClick={() => setExpanded(e => !e)}>\n {expanded ? <ExpandLess /> : <ExpandMore />}\n </IconButton>\n </Box>\n\n {budget > 0 && (\n <Box mt={1.5}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 6, borderRadius: 1 }}\n />\n </Box>\n )}\n\n <Collapse in={expanded}>\n <Box mt={2}>\n {usageLoading ? (\n <Box display=\"flex\" justifyContent=\"center\" p={2}>\n <CircularProgress size={24} />\n </Box>\n ) : dailyData.length > 0 ? (\n <>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={160}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} tickFormatter={v => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(v: number) => [`$${v.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </>\n ) : null}\n\n {team.members_with_roles?.length ? (\n <Box mt={2}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Members\n </Typography>\n <TableContainer>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>User</TableCell>\n <TableCell>Role</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {team.members_with_roles.map(m => (\n <TableRow key={m.user_id}>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: 12 }}>{m.user_id}</TableCell>\n <TableCell>\n <Chip\n label={m.role}\n size=\"small\"\n color={m.role === 'admin' ? 'primary' : 'default'}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n ) : null}\n </Box>\n </Collapse>\n </Paper>\n );\n};\n\ninterface TeamUsageProps {\n teams: TeamInfo[];\n loading: boolean;\n dateRange: DateRange;\n getTeamUsage: (teamId: string) => UsageMetrics | null;\n getTeamUsageLoading: (teamId: string) => boolean;\n}\n\nexport const TeamUsage: React.FC<TeamUsageProps> = ({\n teams,\n loading,\n getTeamUsage,\n getTeamUsageLoading,\n}) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n if (!teams.length) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"disabled\" />\n <Typography color=\"text.secondary\" variant=\"body2\">\n No team membership found in LiteLLM for this account.\n </Typography>\n </Box>\n </Paper>\n );\n }\n\n return (\n <Box>\n <Typography variant=\"h6\" mb={1}>Teams</Typography>\n {teams.map(team => (\n <TeamCard\n key={team.team_id}\n team={team}\n usage={getTeamUsage(team.team_id)}\n usageLoading={getTeamUsageLoading(team.team_id)}\n />\n ))}\n </Box>\n );\n};\n", "import React, { useState, useCallback, useMemo } from 'react';\nimport { Grid, Box, Snackbar, Alert, CircularProgress, Typography, Paper } from '@mui/material';\nimport { useAsync, useAsyncRetry } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { DashboardHeader } from './DashboardHeader';\nimport { KeysTable } from './KeysTable';\nimport { UsageStats } from './UsageStats';\nimport { TeamUsage } from './TeamUsage';\nimport { liteLlmApiRef } from '../api';\nimport { DateRange, GenerateKeyRequest, GenerateKeyResponse, UsageMetrics } from '../types';\n\nexport const LiteLLMPage: React.FC = () => {\n const api = useApi(liteLlmApiRef);\n\n const [dateRange, setDateRange] = useState<DateRange>(() => {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 7);\n return { start, end };\n });\n\n const [snackbar, setSnackbar] = useState<{ message: string; severity: 'success' | 'error' } | null>(null);\n\n // Team usage cache: teamId -> UsageMetrics\n const [teamUsageCache, setTeamUsageCache] = useState<Record<string, UsageMetrics | null>>({});\n const [teamUsageLoading, setTeamUsageLoading] = useState<Record<string, boolean>>({});\n\n const { value: userInfo, loading: userLoading, error: userError } = useAsync(\n () => api.getUserInfo(),\n [api],\n );\n\n const { value: keys, loading: keysLoading, retry: refreshKeys } = useAsyncRetry(\n async () => {\n try {\n return await api.listKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to load keys: ${e.message}`, severity: 'error' });\n return [];\n }\n },\n [api],\n );\n\n const { value: allModels, loading: modelsLoading } = useAsync(\n () => api.listModels().catch(() => []),\n [api],\n );\n\n const { value: allTeams, loading: teamsLoading } = useAsync(\n () => api.getTeams().catch(() => []),\n [api],\n );\n\n // Filter to teams the current user belongs to\n const teams = useMemo(() => {\n if (!allTeams?.length) return [];\n if (!userInfo) return allTeams;\n const userId = userInfo.user_id;\n if (userInfo.teams?.length) {\n return allTeams.filter(t => userInfo.teams!.includes(t.team_id));\n }\n const byMembership = allTeams.filter(t =>\n t.members_with_roles?.some(m => m.user_id === userId),\n );\n return byMembership.length > 0 ? byMembership : allTeams;\n }, [allTeams, userInfo]);\n\n const { value: usage, loading: usageLoading } = useAsync(async () => {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n return api.getUsage(startDate, endDate);\n }, [api, dateRange]);\n\n // Fetch team usage on demand when a team card is expanded\n const loadTeamUsage = useCallback(async (teamId: string) => {\n if (teamUsageCache[teamId] !== undefined || teamUsageLoading[teamId]) return;\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: true }));\n try {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n const data = await api.getTeamUsage(teamId, startDate, endDate);\n setTeamUsageCache(prev => ({ ...prev, [teamId]: data }));\n } catch {\n setTeamUsageCache(prev => ({ ...prev, [teamId]: null }));\n } finally {\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: false }));\n }\n }, [api, dateRange, teamUsageCache, teamUsageLoading]);\n\n // Models available for key generation: intersection of all models with user-level\n // and team-level restrictions. If a user has no model restrictions, all models are allowed.\n const allowedModels = useMemo(() => {\n if (!allModels?.length) return [];\n const userModels = userInfo?.models;\n const teamModels = teams?.flatMap(t => t.models ?? []);\n const hasUserRestriction = userModels && userModels.length > 0;\n const hasTeamRestriction = teamModels && teamModels.length > 0;\n if (!hasUserRestriction && !hasTeamRestriction) return allModels;\n const allowed = new Set([\n ...(hasUserRestriction ? userModels! : allModels.map(m => m.model_name)),\n ...(hasTeamRestriction ? teamModels! : []),\n ]);\n return allModels.filter(m => allowed.has(m.model_name));\n }, [allModels, userInfo, teams]);\n\n const handleGenerateKey = useCallback(\n async (request: GenerateKeyRequest): Promise<GenerateKeyResponse> => {\n const response = await api.generateKey(request);\n setSnackbar({ message: 'Key generated successfully', severity: 'success' });\n refreshKeys();\n return response;\n },\n [api, refreshKeys],\n );\n\n const handleDeleteKey = useCallback(\n async (keyId: string) => {\n try {\n await api.deleteKey(keyId);\n setSnackbar({ message: 'Key revoked successfully', severity: 'success' });\n refreshKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to revoke key: ${e.message}`, severity: 'error' });\n }\n },\n [api, refreshKeys],\n );\n\n const isInitialLoading = userLoading && !userInfo;\n\n if (isInitialLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"50vh\">\n <CircularProgress />\n </Box>\n );\n }\n\n // User exists in Backstage but has no LiteLLM account\n if (userError || !userInfo) {\n const isProvisioningEnabled = (userError as any)?.body?.provisioning === true;\n const hint = (userError as any)?.body?.hint;\n return (\n <Box p={3}>\n <Paper sx={{ p: 3 }}>\n <Typography variant=\"h6\" gutterBottom>Account not provisioned</Typography>\n <Typography color=\"text.secondary\" paragraph>\n Your Backstage account is not linked to a LiteLLM user.\n </Typography>\n {hint ? (\n <Typography variant=\"body2\" color=\"text.secondary\">{hint}</Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {isProvisioningEnabled\n ? 'Auto-provisioning is enabled but failed. Check the backend logs.'\n : 'Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.'}\n </Typography>\n )}\n </Paper>\n </Box>\n );\n }\n\n return (\n <Box p={3}>\n <Grid container spacing={2}>\n <Grid item xs={12}>\n <DashboardHeader userInfo={userInfo} teams={teams ?? []} loading={userLoading || teamsLoading} />\n </Grid>\n\n <Grid item xs={12}>\n <TeamUsage\n teams={teams ?? []}\n loading={teamsLoading}\n dateRange={dateRange}\n getTeamUsage={teamId => {\n if (teamUsageCache[teamId] === undefined) loadTeamUsage(teamId);\n return teamUsageCache[teamId] ?? null;\n }}\n getTeamUsageLoading={teamId => teamUsageLoading[teamId] ?? false}\n />\n </Grid>\n\n <Grid item xs={12}>\n <KeysTable\n keys={keys ?? []}\n models={allowedModels}\n teams={teams ?? []}\n loading={keysLoading || modelsLoading}\n onGenerateKey={handleGenerateKey}\n onDeleteKey={handleDeleteKey}\n />\n </Grid>\n\n <Grid item xs={12}>\n <UsageStats\n usage={usage ?? null}\n models={allModels ?? []}\n dateRange={dateRange}\n onDateRangeChange={setDateRange}\n loading={usageLoading}\n />\n </Grid>\n </Grid>\n\n <Snackbar\n open={!!snackbar}\n autoHideDuration={5000}\n onClose={() => setSnackbar(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n >\n {snackbar ? (\n <Alert severity={snackbar.severity} onClose={() => setSnackbar(null)}>\n {snackbar.message}\n </Alert>\n ) : undefined}\n </Snackbar>\n </Box>\n );\n};\n", "import React from 'react';\nimport { TrendingUp as TrendingUpIcon } from '@mui/icons-material';\nimport {\n createFrontendPlugin,\n createApiExtension,\n createPageExtension,\n createApiFactory,\n createSidebarExtension,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { liteLlmApiRef, LiteLlmApi } from './api';\n\nexport const litellmPlugin = createFrontendPlugin({\n id: 'litellm',\n extensions: [\n createApiExtension({\n factory: createApiFactory({\n api: liteLlmApiRef,\n deps: { fetchApi: fetchApiRef },\n factory: ({ fetchApi }) => new LiteLlmApi(fetchApi),\n }),\n }),\n createSidebarExtension({\n id: 'root',\n title: 'LiteLLM',\n icon: <TrendingUpIcon />,\n defaultPath: '/litellm',\n }),\n createPageExtension({\n defaultPath: '/litellm',\n loader: async () => {\n const { LiteLLMPage } = await import('./components/LiteLLMPage');\n return React.createElement(LiteLLMPage);\n },\n }),\n ],\n});\n", "export { litellmPlugin } from './plugin';\nexport { LiteLLMPage } from './components/LiteLLMPage';\nexport { DashboardHeader } from './components/DashboardHeader';\nexport { KeysTable } from './components/KeysTable';\nexport { UsageStats } from './components/UsageStats';\nexport { TeamUsage } from './components/TeamUsage';\nexport { LiteLlmApi, liteLlmApiRef } from './api';\nexport type { LiteLlmApiInterface } from './api';\nexport * from './types';\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;AAAA,SAAS,oBAA8B;AAAvC,IAWM,UAqBO,eAIA;AApCb;AAAA;AAAA;AAWA,IAAM,WAAN,cAAuB,MAAM;AAAA,MAG3B,YAAY,SAAiB,QAAgB,MAAe;AAC1D,cAAM,OAAO;AACb,aAAK,SAAS;AACd,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAaO,IAAM,gBAAgB,aAAkC;AAAA,MAC7D,IAAI;AAAA,IACN,CAAC;AAEM,IAAM,aAAN,MAAgD;AAAA,MAIrD,YAAY,UAAoB,WAAmB,gBAAgB;AACjE,aAAK,WAAW;AAChB,aAAK,WAAW;AAAA,MAClB;AAAA,MAEA,MAAc,aAAa,UAAmC;AAC5D,YAAI,CAAC,SAAS,IAAI;AAChB,cAAI;AACJ,cAAI;AAAE,mBAAO,MAAM,SAAS,KAAK;AAAA,UAAG,QAAQ;AAAE,mBAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AAAA,UAAG;AAC5F,gBAAM,IAAI,SAAS,GAAG,SAAS,MAAM,IAAI,SAAS,UAAU,IAAI,SAAS,QAAQ,IAAI;AAAA,QACvF;AAAA,MACF;AAAA,MAEA,MAAc,IAAO,MAAc,QAA6C;AAC9E,cAAM,MAAM,IAAI,IAAI,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI,OAAO,SAAS,MAAM;AACrE,YAAI,QAAQ;AACV,iBAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM,IAAI,aAAa,OAAO,KAAK,KAAK,CAAC;AAAA,QACtF;AACA,cAAM,WAAW,MAAM,KAAK,SAAS,MAAM,IAAI,SAAS,CAAC;AACzD,cAAM,KAAK,aAAa,QAAQ;AAChC,eAAO,SAAS,KAAK;AAAA,MACvB;AAAA,MAEA,MAAc,KAAQ,MAAc,MAA2B;AAC7D,cAAM,WAAW,MAAM,KAAK,SAAS,MAAM,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,UACpE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QAC3B,CAAC;AACD,cAAM,KAAK,aAAa,QAAQ;AAChC,eAAO,SAAS,KAAK;AAAA,MACvB;AAAA,MAEA,MAAc,IAAO,MAA0B;AAC7C,cAAM,WAAW,MAAM,KAAK,SAAS,MAAM,GAAG,KAAK,QAAQ,GAAG,IAAI,IAAI;AAAA,UACpE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAChD,CAAC;AACD,cAAM,KAAK,aAAa,QAAQ;AAChC,eAAO,SAAS,KAAK;AAAA,MACvB;AAAA;AAAA;AAAA,MAIA,MAAM,cAAiC;AACrC,eAAO,KAAK,IAAc,YAAY;AAAA,MACxC;AAAA,MAEA,MAAM,WAAkC;AACtC,eAAO,KAAK,IAAkB,OAAO;AAAA,MACvC;AAAA,MAEA,MAAM,YAAY,SAA2D;AAC3E,eAAO,KAAK,KAA0B,kBAAkB,OAAO;AAAA,MACjE;AAAA,MAEA,MAAM,UAAU,OAA8C;AAC5D,eAAO,KAAK,IAA0B,SAAS,mBAAmB,KAAK,CAAC,EAAE;AAAA,MAC5E;AAAA,MAEA,MAAM,aAAmC;AACvC,eAAO,KAAK,IAAiB,SAAS;AAAA,MACxC;AAAA,MAEA,MAAM,WAAgC;AACpC,eAAO,KAAK,IAAgB,QAAQ;AAAA,MACtC;AAAA,MAEA,MAAM,SAAS,WAAmB,SAAwC;AACxE,eAAO,KAAK,IAAkB,UAAU,EAAE,YAAY,WAAW,UAAU,QAAQ,CAAC;AAAA,MACtF;AAAA,MAEA,MAAM,aAAa,QAAgB,WAAmB,SAAwC;AAC5F,eAAO,KAAK,IAAkB,UAAU,mBAAmB,MAAM,CAAC,UAAU;AAAA,UAC1E,YAAY;AAAA,UACZ,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,IACF;AAAA;AAAA;;;ACtHA,OAAO,WAAW;AAClB,SAAS,KAAK,YAAY,gBAAgB,OAAO,YAAY;AAC7D,SAAS,eAAe;AAFxB,IAWa;AAXb;AAAA;AAAA;AAWO,IAAM,kBAAkD,CAAC,EAAE,UAAU,OAAO,QAAQ,MAAM;AAC/F,UAAI,SAAS;AACX,eACE,oCAAC,SAAM,IAAI,EAAE,GAAG,GAAG,IAAI,EAAE,KACvB,oCAAC,oBAAe,CAClB;AAAA,MAEJ;AAEA,YAAM,cAAc,SAAS,cAAc,SAAS,SAAS,SAAS;AACtE,YAAM,SAAS,SAAS,cAAc;AACtC,YAAM,QAAQ,SAAS,SAAS,SAAS,iBAAiB;AAC1D,YAAM,YAAY,SAAS,IAAI,KAAK,IAAK,QAAQ,SAAU,KAAK,GAAG,IAAI;AACvE,YAAM,SAAS,SAAS,KAAK,SAAS;AACtC,YAAM,SAAS,SAAS,KAAK,SAAS,SAAS,OAAO,CAAC;AAEvD,aACE,oCAAC,SAAM,IAAI,EAAE,GAAG,GAAG,IAAI,EAAE,KACvB,oCAAC,OAAI,SAAQ,QAAO,YAAW,cAAa,KAAK,GAAG,UAAS,UAC3D,oCAAC,OAAI,UAAU,KACb,oCAAC,cAAW,SAAQ,QAAM,WAAY,GACtC,oCAAC,cAAW,SAAQ,WAAU,OAAM,oBACjC,SAAS,OACZ,GAEC,MAAM,SAAS,KACd,oCAAC,OAAI,SAAQ,QAAO,KAAK,MAAM,UAAS,QAAO,IAAI,KAChD,MAAM,IAAI,UACT;AAAA,QAAC;AAAA;AAAA,UACC,KAAK,KAAK;AAAA,UACV,OAAO,KAAK,cAAc,KAAK;AAAA,UAC/B,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,OAAM;AAAA;AAAA,MACR,CACD,CACH,CAEJ,GAEC,SAAS,KACR,oCAAC,OAAI,UAAU,OACb,oCAAC,OAAI,SAAQ,QAAO,gBAAe,iBAAgB,IAAI,OACrD,oCAAC,cAAW,SAAQ,WAAQ,KACxB,MAAM,QAAQ,CAAC,GAAE,QAAK,OAAO,QAAQ,CAAC,CAC1C,GACC,UAAU,oCAAC,QAAK,MAAM,oCAAC,aAAQ,GAAI,OAAM,eAAc,MAAK,SAAQ,OAAM,SAAQ,GAClF,UAAU,oCAAC,QAAK,OAAM,cAAa,MAAK,SAAQ,OAAM,WAAU,CACnE,GACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,OAAO;AAAA,UACP,OAAO,SAAS,UAAU,SAAS,YAAY;AAAA,UAC/C,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE;AAAA;AAAA,MACnC,CACF,CAEJ,CACF;AAAA,IAEJ;AAAA;AAAA;;;ACvEA,OAAOA,UAAS,gBAAgB;AAChC;AAAA,EACE,SAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAAC;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,aAAa,QAAQ,KAAK,YAAY,qBAAqB;AAvBpE,IAmCM,SAKA,YAQA,WASO;AAzDb;AAAA;AAAA;AAmCA,IAAM,UAAU,CAAC,QAAwB;AACvC,UAAI,IAAI,UAAU,EAAG,QAAO;AAC5B,aAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;AAAA,IAC9C;AAEA,IAAM,aAAa,CAAC,YAA4B;AAC9C,UAAI;AACF,eAAO,IAAI,KAAK,OAAO,EAAE,mBAAmB;AAAA,MAC9C,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAEA,IAAM,YAAY,OAA2B;AAAA,MAC3C,OAAO;AAAA,MACP,QAAQ,CAAC;AAAA,MACT,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAEO,IAAM,YAAsC,CAAC;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,MAAM;AACJ,YAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,KAAK;AAChE,YAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AACpE,YAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,IAAI;AAClE,YAAM,CAAC,UAAU,WAAW,IAAI,SAA6B,UAAU,CAAC;AACxE,YAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAElD,YAAM,iBAAiB,OAAO,OAAO,QAAM,SAAS,UAAU,CAAC,GAAG,SAAS,EAAE,UAAU,CAAC;AACxF,YAAM,eAAe,MAAM,KAAK,OAAK,EAAE,YAAY,SAAS,OAAO,KAAK;AAExE,YAAM,iBAAiB,YAAY;AACjC,sBAAc,IAAI;AAClB,YAAI;AACF,gBAAM,WAAW,MAAM,cAAc,QAAQ;AAC7C,yBAAe,SAAS,GAAG;AAC3B,sBAAY,UAAU,CAAC;AAAA,QACzB,SAAS,OAAO;AACd,kBAAQ,MAAM,2BAA2B,KAAK;AAAA,QAChD,UAAE;AACA,wBAAc,KAAK;AAAA,QACrB;AAAA,MACF;AAEA,YAAM,mBAAmB,MAAM;AAC7B,6BAAqB,KAAK;AAC1B,uBAAe,IAAI;AACnB,oBAAY,UAAU,CAAC;AAAA,MACzB;AAEA,YAAM,kBAAkB,CAAC,SAAiB;AACxC,kBAAU,UAAU,UAAU,IAAI;AAAA,MACpC;AAEA,aACE,gBAAAJ,OAAA,cAAAA,OAAA,gBACE,gBAAAA,OAAA,cAACC,QAAA,EAAM,IAAI,EAAE,IAAI,EAAE,KACjB,gBAAAD,OAAA,cAACE,MAAA,EAAI,SAAQ,QAAO,gBAAe,iBAAgB,YAAW,UAAS,GAAG,KACxE,gBAAAF,OAAA,cAACG,aAAA,EAAW,SAAQ,QAAK,cAAY,GACrC,gBAAAH,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,OAAM;AAAA,UACN,WAAW,gBAAAA,OAAA,cAAC,SAAI;AAAA,UAChB,SAAS,MAAM,qBAAqB,IAAI;AAAA;AAAA,QACzC;AAAA,MAED,CACF,GAEA,gBAAAA,OAAA,cAAC,sBACC,gBAAAA,OAAA,cAAC,aACC,gBAAAA,OAAA,cAAC,iBACC,gBAAAA,OAAA,cAAC,gBACC,gBAAAA,OAAA,cAAC,iBAAU,OAAK,GAChB,gBAAAA,OAAA,cAAC,iBAAU,KAAG,GACd,gBAAAA,OAAA,cAAC,iBAAU,SAAO,GAClB,gBAAAA,OAAA,cAAC,iBAAU,OAAK,GAChB,gBAAAA,OAAA,cAAC,iBAAU,QAAM,GACjB,gBAAAA,OAAA,cAAC,iBAAU,WAAS,GACpB,gBAAAA,OAAA,cAAC,iBAAU,QAAM,GACjB,gBAAAA,OAAA,cAAC,aAAU,OAAM,WAAQ,SAAO,CAClC,CACF,GACA,gBAAAA,OAAA,cAAC,iBACE,UACC,gBAAAA,OAAA,cAAC,gBACC,gBAAAA,OAAA,cAAC,aAAU,SAAS,GAAG,OAAM,YAC3B,gBAAAA,OAAA,cAAC,oBAAiB,MAAM,IAAI,CAC9B,CACF,IACE,KAAK,WAAW,IAClB,gBAAAA,OAAA,cAAC,gBACC,gBAAAA,OAAA,cAAC,aAAU,SAAS,GAAG,OAAM,YAC3B,gBAAAA,OAAA,cAACG,aAAA,EAAW,OAAM,oBAAiB,eAAa,CAClD,CACF,IAEA,KAAK,IAAI,CAAC,QACR,gBAAAH,OAAA,cAAC,YAAS,KAAK,IAAI,OACjB,gBAAAA,OAAA,cAAC,iBAAW,IAAI,aAAa,GAAI,GACjC,gBAAAA,OAAA,cAAC,iBACC,gBAAAA,OAAA,cAACE,MAAA,EAAI,SAAQ,QAAO,YAAW,UAAS,KAAK,OAC3C,gBAAAF,OAAA,cAACG,aAAA,EAAW,SAAQ,SAAQ,WAAU,QAAO,IAAI,EAAE,YAAY,YAAY,KACxE,iBAAiB,IAAI,MAAM,IAAI,MAAM,QAAQ,IAAI,GAAG,CACvD,GACA,gBAAAH,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,gBAAgB,iBAAiB,IAAI,MAAM,OAAO,IAAI,GAAG;AAAA;AAAA,QAEvE,iBAAiB,IAAI,MAAM,gBAAAA,OAAA,cAAC,mBAAc,IAAK,gBAAAA,OAAA,cAAC,gBAAW;AAAA,MAC9D,GACA,gBAAAA,OAAA,cAAC,cAAW,MAAK,SAAQ,SAAS,MAAM,gBAAgB,IAAI,GAAG,KAC7D,gBAAAA,OAAA,cAAC,eAAY,UAAS,SAAQ,CAChC,CACF,CACF,GACA,gBAAAA,OAAA,cAAC,iBAAW,WAAW,IAAI,UAAU,CAAE,GACvC,gBAAAA,OAAA,cAAC,iBAAU,KAAE,IAAI,OAAO,QAAQ,CAAC,KAAK,MAAO,GAC7C,gBAAAA,OAAA,cAAC,iBAAW,IAAI,aAAa,IAAI,IAAI,UAAU,KAAK,GAAI,GACxD,gBAAAA,OAAA,cAAC,iBAAW,IAAI,aAAa,GAAI,GACjC,gBAAAA,OAAA,cAAC,iBACC,gBAAAA,OAAA,cAACE,MAAA,EAAI,SAAQ,QAAO,KAAK,KAAK,UAAS,UACpC,IAAI,QAAQ,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,UAC5B,gBAAAF,OAAA,cAACI,OAAA,EAAK,KAAK,OAAO,OAAO,OAAO,MAAK,SAAQ,CAC9C,IACC,IAAI,QAAQ,UAAU,KAAK,KAC3B,gBAAAJ,OAAA,cAACI,OAAA,EAAK,OAAO,KAAK,IAAI,QAAQ,UAAU,KAAK,CAAC,IAAI,MAAK,SAAQ,SAAQ,YAAW,CAEtF,CACF,GACA,gBAAAJ,OAAA,cAAC,aAAU,OAAM,WACf,gBAAAA,OAAA,cAAC,cAAW,OAAM,SAAQ,SAAS,MAAM,YAAY,IAAI,GAAG,KAC1D,gBAAAA,OAAA,cAAC,YAAO,CACV,CACF,CACF,CACD,CAEL,CACF,CACF,CACF,GAEA,gBAAAA,OAAA,cAAC,UAAO,MAAM,mBAAmB,SAAS,kBAAkB,UAAS,MAAK,WAAS,QACjF,gBAAAA,OAAA,cAAC,mBAAa,cAAc,kBAAkB,kBAAmB,GACjE,gBAAAA,OAAA,cAAC,qBACE,cACC,gBAAAA,OAAA,cAACE,MAAA,MACC,gBAAAF,OAAA,cAACG,aAAA,EAAW,SAAQ,SAAQ,OAAM,kBAAiB,cAAY,QAAC,uDAEhE,GACA,gBAAAH,OAAA;AAAA,QAACE;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,YAAW;AAAA,UACX,KAAK;AAAA,UACL,IAAI;AAAA,UACJ,GAAG;AAAA,UACH,IAAI,EAAE,iBAAiB,gBAAgB,cAAc,EAAE;AAAA;AAAA,QAEvD,gBAAAF,OAAA;AAAA,UAACG;AAAA,UAAA;AAAA,YACC,WAAU;AAAA,YACV,IAAI,EAAE,YAAY,aAAa,WAAW,aAAa,MAAM,EAAE;AAAA;AAAA,UAE9D;AAAA,QACH;AAAA,QACA,gBAAAH,OAAA,cAAC,cAAW,SAAS,MAAM,gBAAgB,WAAW,KACpD,gBAAAA,OAAA,cAAC,iBAAY,CACf;AAAA,MACF,CACF,IAEA,gBAAAA,OAAA,cAACE,MAAA,EAAI,SAAQ,QAAO,eAAc,UAAS,KAAK,GAAG,IAAI,KACrD,gBAAAF,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,OAAO,SAAS,SAAS;AAAA,UACzB,UAAU,CAAC,MAAM,YAAY,EAAE,GAAG,UAAU,OAAO,EAAE,OAAO,MAAM,CAAC;AAAA,UACnE,WAAS;AAAA;AAAA,MACX,GACA,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,QAAM;AAAA,UACN,OAAM;AAAA,UACN,OAAO,SAAS,YAAY;AAAA,UAC5B,UAAU,CAAC,MAAM,YAAY,EAAE,GAAG,UAAU,UAAU,EAAE,OAAO,MAAM,CAAC;AAAA,UACtE,WAAS;AAAA;AAAA,QAET,gBAAAA,OAAA,cAAC,YAAS,OAAM,QAAK,OAAK;AAAA,QAC1B,gBAAAA,OAAA,cAAC,YAAS,OAAM,QAAK,QAAM;AAAA,QAC3B,gBAAAA,OAAA,cAAC,YAAS,OAAM,SAAM,SAAO;AAAA,QAC7B,gBAAAA,OAAA,cAAC,YAAS,OAAM,SAAM,SAAO;AAAA,QAC7B,gBAAAA,OAAA,cAAC,YAAS,OAAM,QAAK,QAAM;AAAA,MAC7B,GAEC,MAAM,SAAS,KACd,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,gBAAgB,OAAK,EAAE,cAAc,EAAE;AAAA,UACvC,OAAO;AAAA,UACP,UAAU,CAAC,IAAI,SACb,YAAY,EAAE,GAAG,UAAU,SAAS,MAAM,QAAQ,CAAC;AAAA,UAErD,aAAa,YACX,gBAAAA,OAAA,cAAC,aAAW,GAAG,QAAQ,OAAM,mBAAkB,WAAS,MAAC;AAAA;AAAA,MAE7D,GAGD,OAAO,SAAS,KACf,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,UAAQ;AAAA,UACR,SAAS;AAAA,UACT,SAAS,OAAK,EAAE,QAAQ;AAAA,UACxB,gBAAgB,OAAK,EAAE;AAAA,UACvB,OAAO;AAAA,UACP,UAAU,CAAC,IAAI,aACb,YAAY,EAAE,GAAG,UAAU,QAAQ,SAAS,IAAI,OAAK,EAAE,UAAU,EAAE,CAAC;AAAA,UAEtE,cAAc,CAAC,OAAO,MACpB,gBAAAA,OAAA,cAAC,QAAI,GAAG,SACL,EAAE,YACF,EAAE,6BAA6B,cAC/B,EAAE,mBAAmB,kBACxB;AAAA,UAEF,aAAa,YACX,gBAAAA,OAAA,cAAC,aAAW,GAAG,QAAQ,OAAM,UAAS,WAAS,MAAC;AAAA;AAAA,MAEpD,GAGF,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,MAAK;AAAA,UACL,OAAO,SAAS,cAAc;AAAA,UAC9B,UAAU,CAAC,MACT,YAAY,EAAE,GAAG,UAAU,YAAY,EAAE,OAAO,QAAQ,OAAO,EAAE,OAAO,KAAK,IAAI,OAAU,CAAC;AAAA,UAE9F,WAAS;AAAA;AAAA,MACX,GACA,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAM;AAAA,UACN,MAAK;AAAA,UACL,OAAO,SAAS,aAAa;AAAA,UAC7B,UAAU,CAAC,MACT,YAAY,EAAE,GAAG,UAAU,WAAW,EAAE,OAAO,QAAQ,OAAO,EAAE,OAAO,KAAK,IAAI,OAAU,CAAC;AAAA,UAE7F,WAAS;AAAA;AAAA,MACX,CACF,CAEJ,GACA,gBAAAA,OAAA,cAAC,qBACC,gBAAAA,OAAA,cAAC,UAAO,SAAS,oBAAmB,cAAc,SAAS,QAAS,GACnE,CAAC,eACA,gBAAAA,OAAA,cAAC,UAAO,SAAS,gBAAgB,SAAQ,aAAY,OAAM,WAAU,UAAU,cAC5E,aAAa,gBAAAA,OAAA,cAAC,oBAAiB,MAAM,IAAI,IAAK,UACjD,CAEJ,CACF,CACF;AAAA,IAEJ;AAAA;AAAA;;;AChTA,OAAOK,UAAS,YAAAC,iBAAgB;AAChC;AAAA,EACE,SAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,EACA,oBAAAC;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAvBP,IAoCa;AApCb;AAAA;AAAA;AAoCO,IAAM,aAAwC,CAAC;AAAA,MACpD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,MAAM;AACJ,YAAM,CAAC,gBAAgB,iBAAiB,IAAIL,UAAqB,IAAI;AACrE,YAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAiB,KAAK;AAEhE,YAAM,qBAAqB,CAAC,WAAuB;AACjD,0BAAkB,MAAM;AACxB,cAAM,MAAM,oBAAI,KAAK;AACrB,cAAM,QAAQ,oBAAI,KAAK;AACvB,YAAI,WAAW,QAAS,OAAM,SAAS,GAAG,GAAG,GAAG,CAAC;AAAA,iBACxC,WAAW,KAAM,OAAM,QAAQ,MAAM,QAAQ,IAAI,CAAC;AAAA,iBAClD,WAAW,MAAO,OAAM,QAAQ,MAAM,QAAQ,IAAI,EAAE;AAC7D,0BAAkB,EAAE,OAAO,IAAI,CAAC;AAAA,MAClC;AAEA,UAAI,SAAS;AACX,eACE,gBAAAD,OAAA,cAACE,QAAA,EAAM,IAAI,EAAE,GAAG,EAAE,KAChB,gBAAAF,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,gBAAe,UAAS,GAAG,KAC7C,gBAAAH,OAAA,cAACM,mBAAA,IAAiB,CACpB,CACF;AAAA,MAEJ;AAEA,YAAM,YACJ,OAAO,aAAa,IAAI,CAAC,OAAO;AAAA,QAC9B,MAAM,EAAE;AAAA,QACR,OAAO,EAAE;AAAA,QACT,cAAc,EAAE;AAAA,QAChB,kBAAkB,EAAE;AAAA,QACpB,aAAa,EAAE;AAAA,MACjB,EAAE,KAAK,CAAC;AAEV,YAAM,mBAAmB,OAAO,QAAQ,OAAO,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO;AAAA,QAC3F;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,cAAc,KAAK;AAAA,QACnB,kBAAkB,KAAK;AAAA,MACzB,EAAE;AAEF,YAAM,oBACJ,kBAAkB,QACd,mBACA,iBAAiB,OAAO,CAAC,MAAM,EAAE,UAAU,aAAa;AAE9D,aACE,gBAAAN,OAAA,cAACE,QAAA,EAAM,IAAI,EAAE,GAAG,EAAE,KAChB,gBAAAF,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,gBAAe,iBAAgB,YAAW,UAAS,IAAI,KACzE,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,QAAK,iBAAe,GACxC,gBAAAJ,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,KAAK,GAAG,YAAW,YACrC,gBAAAH,OAAA,cAAC,eAAY,MAAK,SAAQ,IAAI,EAAE,UAAU,IAAI,KAC5C,gBAAAA,OAAA,cAAC,kBAAW,QAAM,GAClB,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,OAAM;AAAA,UACN,UAAU,CAAC,MAAM,mBAAmB,EAAE,OAAO,KAAmB;AAAA;AAAA,QAEhE,gBAAAA,OAAA,cAACK,WAAA,EAAS,OAAM,WAAQ,OAAK;AAAA,QAC7B,gBAAAL,OAAA,cAACK,WAAA,EAAS,OAAM,QAAK,aAAW;AAAA,QAChC,gBAAAL,OAAA,cAACK,WAAA,EAAS,OAAM,SAAM,cAAY;AAAA,MACpC,CACF,GACA,gBAAAL,OAAA,cAAC,eAAY,MAAK,SAAQ,IAAI,EAAE,UAAU,IAAI,KAC5C,gBAAAA,OAAA,cAAC,kBAAW,OAAK,GACjB,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,OAAM;AAAA,UACN,UAAU,CAAC,MAAM,iBAAiB,EAAE,OAAO,KAAK;AAAA;AAAA,QAEhD,gBAAAA,OAAA,cAACK,WAAA,EAAS,OAAM,SAAM,YAAU;AAAA,QAC/B,OAAO,IAAI,CAAC,MACX,gBAAAL,OAAA,cAACK,WAAA,EAAS,KAAK,EAAE,YAAY,OAAO,EAAE,cACnC,EAAE,UACL,CACD;AAAA,MACH,CACF,CACF,CACF,GAEA,gBAAAL,OAAA,cAAC,QAAK,WAAS,MAAC,SAAS,KACvB,gBAAAA,OAAA,cAAC,QAAK,MAAI,MAAC,IAAI,IAAI,IAAI,KACrB,gBAAAA,OAAA,cAACI,aAAA,EAAW,SAAQ,aAAY,OAAM,kBAAiB,cAAY,QAAC,aAEpE,GACA,gBAAAJ,OAAA,cAACG,MAAA,EAAI,QAAQ,OACX,gBAAAH,OAAA,cAAC,uBAAoB,OAAM,QAAO,QAAO,UACvC,gBAAAA,OAAA,cAAC,aAAU,MAAM,aACf,gBAAAA,OAAA,cAAC,iBAAc,iBAAgB,OAAM,GACrC,gBAAAA,OAAA,cAAC,SAAM,SAAQ,QAAO,MAAM,EAAE,UAAU,GAAG,GAAG,GAC9C,gBAAAA,OAAA,cAAC,SAAM,MAAM,EAAE,UAAU,GAAG,GAAG,eAAe,CAAC,MAAM,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,GACzE,gBAAAA,OAAA,cAAC,WAAQ,WAAW,CAAC,UAAkB,CAAC,IAAI,MAAM,QAAQ,CAAC,CAAC,IAAI,OAAO,GAAG,GAC1E,gBAAAA,OAAA,cAAC,QAAK,MAAK,YAAW,SAAQ,SAAQ,QAAO,WAAU,MAAK,WAAU,aAAa,KAAK,CAC1F,CACF,CACF,CACF,GAEA,gBAAAA,OAAA,cAAC,QAAK,MAAI,MAAC,IAAI,IAAI,IAAI,KACrB,gBAAAA,OAAA,cAACI,aAAA,EAAW,SAAQ,aAAY,OAAM,kBAAiB,cAAY,QAAC,sBAEpE,GACA,gBAAAJ,OAAA,cAACG,MAAA,EAAI,QAAQ,OACX,gBAAAH,OAAA,cAAC,uBAAoB,OAAM,QAAO,QAAO,UACvC,gBAAAA,OAAA,cAAC,YAAS,MAAM,qBACd,gBAAAA,OAAA,cAAC,iBAAc,iBAAgB,OAAM,GACrC,gBAAAA,OAAA,cAAC,SAAM,SAAQ,SAAQ,MAAM,EAAE,UAAU,GAAG,GAAG,GAC/C,gBAAAA,OAAA,cAAC,SAAM,MAAM,EAAE,UAAU,GAAG,GAAG,GAC/B,gBAAAA,OAAA,cAAC,aAAQ,GACT,gBAAAA,OAAA,cAAC,YAAO,GACR,gBAAAA,OAAA,cAAC,OAAI,SAAQ,gBAAe,MAAK,iBAAgB,MAAK,WAAU,SAAQ,KAAI,GAC5E,gBAAAA,OAAA,cAAC,OAAI,SAAQ,oBAAmB,MAAK,qBAAoB,MAAK,WAAU,SAAQ,KAAI,CACtF,CACF,CACF,CACF,GAEA,gBAAAA,OAAA,cAAC,QAAK,MAAI,MAAC,IAAI,MACb,gBAAAA,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,KAAK,GAAG,UAAS,UACnC,gBAAAH,OAAA,cAACG,MAAA,MACC,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAAiB,aAAW,GAC9D,gBAAAJ,OAAA,cAACI,aAAA,EAAW,SAAQ,QAAK,KAAE,OAAO,aAAa,QAAQ,CAAC,KAAK,MAAO,CACtE,GACA,gBAAAJ,OAAA,cAACG,MAAA,MACC,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAAiB,cAAY,GAC/D,gBAAAJ,OAAA,cAACI,aAAA,EAAW,SAAQ,QAAM,OAAO,cAAc,eAAe,KAAK,GAAI,CACzE,GACA,gBAAAJ,OAAA,cAACG,MAAA,MACC,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAAiB,eAAa,GAChE,gBAAAJ,OAAA,cAACI,aAAA,EAAW,SAAQ,QAAM,OAAO,eAAe,eAAe,KAAK,GAAI,CAC1E,GACA,gBAAAJ,OAAA,cAACG,MAAA,MACC,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAAiB,mBAAiB,GACpE,gBAAAJ,OAAA,cAACI,aAAA,EAAW,SAAQ,QAAM,OAAO,mBAAmB,eAAe,KAAK,GAAI,CAC9E,CACF,CACF,CACF,CACF;AAAA,IAEJ;AAAA;AAAA;;;ACtLA,OAAOG,UAAS,YAAAC,iBAAgB;AAChC;AAAA,EACE,SAAAC;AAAA,EACA,OAAAC;AAAA,EACA,cAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,aAAAC;AAAA,EACA,aAAAC;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,EACA,kBAAAC;AAAA,EACA;AAAA,EACA,cAAAC;AAAA,EACA,oBAAAC;AAAA,OACK;AACP,SAAS,YAAY,YAAY,aAAa;AAC9C;AAAA,EACE,aAAAC;AAAA,EACA,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,SAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,WAAAC;AAAA,EACA,uBAAAC;AAAA,OACK;AA1BP,IAmCM,UA0HO;AA7Jb;AAAA;AAAA;AAmCA,IAAM,WAAoC,CAAC,EAAE,MAAM,OAAO,aAAa,MAAM;AAC3E,YAAM,CAAC,UAAU,WAAW,IAAIpB,UAAS,KAAK;AAE9C,YAAM,SAAS,KAAK,cAAc;AAClC,YAAM,QAAQ,KAAK,SAAS;AAC5B,YAAM,YAAY,SAAS,IAAI,KAAK,IAAK,QAAQ,SAAU,KAAK,GAAG,IAAI;AACvE,YAAM,SAAS,SAAS,KAAK,SAAS;AACtC,YAAM,SAAS,SAAS,KAAK,SAAS,SAAS,OAAO,CAAC;AAEvD,YAAM,YAAY,OAAO,aAAa,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC;AAEvF,aACE,gBAAAD,OAAA,cAACE,QAAA,EAAM,SAAQ,YAAW,IAAI,EAAE,GAAG,GAAG,IAAI,EAAE,KAC1C,gBAAAF,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,YAAW,UAAS,KAAK,KAC3C,gBAAAH,OAAA,cAAC,SAAM,OAAM,UAAS,GACtB,gBAAAA,OAAA,cAACG,MAAA,EAAI,UAAU,KACb,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,aAAY,YAAY,OACzC,KAAK,cAAc,KAAK,OAC3B,GACC,KAAK,QAAQ,SACZ,gBAAAJ,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,KAAK,KAAK,UAAS,QAAO,IAAI,OAC/C,KAAK,OAAO,IAAI,OACf,gBAAAH,OAAA,cAACM,OAAA,EAAK,KAAK,GAAG,OAAO,GAAG,MAAK,SAAQ,SAAQ,YAAW,CACzD,CACH,IAEA,gBAAAN,OAAA,cAACI,aAAA,EAAW,SAAQ,WAAU,OAAM,oBAAiB,YAAU,CAEnE,GACA,gBAAAJ,OAAA,cAACa,aAAA,EAAW,MAAK,SAAQ,SAAS,MAAM,YAAY,OAAK,CAAC,CAAC,KACxD,WAAW,gBAAAb,OAAA,cAAC,gBAAW,IAAK,gBAAAA,OAAA,cAAC,gBAAW,CAC3C,CACF,GAEC,SAAS,KACR,gBAAAA,OAAA,cAACG,MAAA,EAAI,IAAI,OACP,gBAAAH,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,gBAAe,iBAAgB,IAAI,OACrD,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,WAAQ,KACxB,MAAM,QAAQ,CAAC,GAAE,QAAK,OAAO,QAAQ,CAAC,CAC1C,GACC,UAAU,gBAAAJ,OAAA,cAACM,OAAA,EAAK,OAAM,eAAc,MAAK,SAAQ,OAAM,SAAQ,GAC/D,UAAU,gBAAAN,OAAA,cAACM,OAAA,EAAK,OAAM,cAAa,MAAK,SAAQ,OAAM,WAAU,CACnE,GACA,gBAAAN,OAAA;AAAA,QAACK;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,OAAO;AAAA,UACP,OAAO,SAAS,UAAU,SAAS,YAAY;AAAA,UAC/C,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE;AAAA;AAAA,MACnC,CACF,GAGF,gBAAAL,OAAA,cAAC,YAAS,IAAI,YACZ,gBAAAA,OAAA,cAACG,MAAA,EAAI,IAAI,KACN,eACC,gBAAAH,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,gBAAe,UAAS,GAAG,KAC7C,gBAAAH,OAAA,cAACc,mBAAA,EAAiB,MAAM,IAAI,CAC9B,IACE,UAAU,SAAS,IACrB,gBAAAd,OAAA,cAAAA,OAAA,gBACE,gBAAAA,OAAA,cAACI,aAAA,EAAW,SAAQ,aAAY,OAAM,kBAAiB,cAAY,QAAC,aAEpE,GACA,gBAAAJ,OAAA,cAACG,MAAA,EAAI,QAAQ,OACX,gBAAAH,OAAA,cAACqB,sBAAA,EAAoB,OAAM,QAAO,QAAO,UACvC,gBAAArB,OAAA,cAACe,YAAA,EAAU,MAAM,aACf,gBAAAf,OAAA,cAACmB,gBAAA,EAAc,iBAAgB,OAAM,GACrC,gBAAAnB,OAAA,cAACiB,QAAA,EAAM,SAAQ,QAAO,MAAM,EAAE,UAAU,GAAG,GAAG,GAC9C,gBAAAjB,OAAA,cAACkB,QAAA,EAAM,MAAM,EAAE,UAAU,GAAG,GAAG,eAAe,OAAK,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,GACvE,gBAAAlB,OAAA,cAACoB,UAAA,EAAQ,WAAW,CAAC,MAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,IAAI,OAAO,GAAG,GAClE,gBAAApB,OAAA,cAACgB,OAAA,EAAK,MAAK,YAAW,SAAQ,SAAQ,QAAO,WAAU,MAAK,WAAU,aAAa,KAAK,CAC1F,CACF,CACF,CACF,IACE,MAEH,KAAK,oBAAoB,SACxB,gBAAAhB,OAAA,cAACG,MAAA,EAAI,IAAI,KACP,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,aAAY,OAAM,kBAAiB,cAAY,QAAC,SAEpE,GACA,gBAAAJ,OAAA,cAACY,iBAAA,MACC,gBAAAZ,OAAA,cAACO,QAAA,EAAM,MAAK,WACV,gBAAAP,OAAA,cAACU,YAAA,MACC,gBAAAV,OAAA,cAACW,WAAA,MACC,gBAAAX,OAAA,cAACS,YAAA,MAAU,MAAI,GACf,gBAAAT,OAAA,cAACS,YAAA,MAAU,MAAI,CACjB,CACF,GACA,gBAAAT,OAAA,cAACQ,YAAA,MACE,KAAK,mBAAmB,IAAI,OAC3B,gBAAAR,OAAA,cAACW,WAAA,EAAS,KAAK,EAAE,WACf,gBAAAX,OAAA,cAACS,YAAA,EAAU,IAAI,EAAE,YAAY,aAAa,UAAU,GAAG,KAAI,EAAE,OAAQ,GACrE,gBAAAT,OAAA,cAACS,YAAA,MACC,gBAAAT,OAAA;AAAA,QAACM;AAAA,QAAA;AAAA,UACC,OAAO,EAAE;AAAA,UACT,MAAK;AAAA,UACL,OAAO,EAAE,SAAS,UAAU,YAAY;AAAA;AAAA,MAC1C,CACF,CACF,CACD,CACH,CACF,CACF,CACF,IACE,IACN,CACF,CACF;AAAA,IAEJ;AAUO,IAAM,YAAsC,CAAC;AAAA,MAClD;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,MAAM;AACJ,UAAI,SAAS;AACX,eACE,gBAAAN,OAAA,cAACE,QAAA,EAAM,IAAI,EAAE,GAAG,EAAE,KAChB,gBAAAF,OAAA,cAACK,iBAAA,IAAe,CAClB;AAAA,MAEJ;AAEA,UAAI,CAAC,MAAM,QAAQ;AACjB,eACE,gBAAAL,OAAA,cAACE,QAAA,EAAM,IAAI,EAAE,GAAG,EAAE,KAChB,gBAAAF,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,YAAW,UAAS,KAAK,KAC3C,gBAAAH,OAAA,cAAC,SAAM,OAAM,YAAW,GACxB,gBAAAA,OAAA,cAACI,aAAA,EAAW,OAAM,kBAAiB,SAAQ,WAAQ,uDAEnD,CACF,CACF;AAAA,MAEJ;AAEA,aACE,gBAAAJ,OAAA,cAACG,MAAA,MACC,gBAAAH,OAAA,cAACI,aAAA,EAAW,SAAQ,MAAK,IAAI,KAAG,OAAK,GACpC,MAAM,IAAI,UACT,gBAAAJ,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK,KAAK;AAAA,UACV;AAAA,UACA,OAAO,aAAa,KAAK,OAAO;AAAA,UAChC,cAAc,oBAAoB,KAAK,OAAO;AAAA;AAAA,MAChD,CACD,CACH;AAAA,IAEJ;AAAA;AAAA;;;ACrMA;AAAA;AAAA;AAAA;AAAA,OAAOsB,UAAS,YAAAC,WAAU,aAAa,eAAe;AACtD,SAAS,QAAAC,OAAM,OAAAC,MAAK,UAAU,OAAO,oBAAAC,mBAAkB,cAAAC,aAAY,SAAAC,cAAa;AAChF,SAAS,UAAU,qBAAqB;AACxC,SAAS,cAAc;AAHvB,IAWa;AAXb;AAAA;AAAA;AAIA;AACA;AACA;AACA;AACA;AAGO,IAAM,cAAwB,MAAM;AACzC,YAAM,MAAM,OAAO,aAAa;AAEhC,YAAM,CAAC,WAAW,YAAY,IAAIL,UAAoB,MAAM;AAC1D,cAAM,MAAM,oBAAI,KAAK;AACrB,cAAM,QAAQ,oBAAI,KAAK;AACvB,cAAM,QAAQ,MAAM,QAAQ,IAAI,CAAC;AACjC,eAAO,EAAE,OAAO,IAAI;AAAA,MACtB,CAAC;AAED,YAAM,CAAC,UAAU,WAAW,IAAIA,UAAoE,IAAI;AAGxG,YAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA8C,CAAC,CAAC;AAC5F,YAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAkC,CAAC,CAAC;AAEpF,YAAM,EAAE,OAAO,UAAU,SAAS,aAAa,OAAO,UAAU,IAAI;AAAA,QAClE,MAAM,IAAI,YAAY;AAAA,QACtB,CAAC,GAAG;AAAA,MACN;AAEA,YAAM,EAAE,OAAO,MAAM,SAAS,aAAa,OAAO,YAAY,IAAI;AAAA,QAChE,YAAY;AACV,cAAI;AACF,mBAAO,MAAM,IAAI,SAAS;AAAA,UAC5B,SAAS,GAAQ;AACf,wBAAY,EAAE,SAAS,wBAAwB,EAAE,OAAO,IAAI,UAAU,QAAQ,CAAC;AAC/E,mBAAO,CAAC;AAAA,UACV;AAAA,QACF;AAAA,QACA,CAAC,GAAG;AAAA,MACN;AAEA,YAAM,EAAE,OAAO,WAAW,SAAS,cAAc,IAAI;AAAA,QACnD,MAAM,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC,CAAC;AAAA,QACrC,CAAC,GAAG;AAAA,MACN;AAEA,YAAM,EAAE,OAAO,UAAU,SAAS,aAAa,IAAI;AAAA,QACjD,MAAM,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC,CAAC;AAAA,QACnC,CAAC,GAAG;AAAA,MACN;AAGA,YAAM,QAAQ,QAAQ,MAAM;AAC1B,YAAI,CAAC,UAAU,OAAQ,QAAO,CAAC;AAC/B,YAAI,CAAC,SAAU,QAAO;AACtB,cAAM,SAAS,SAAS;AACxB,YAAI,SAAS,OAAO,QAAQ;AAC1B,iBAAO,SAAS,OAAO,OAAK,SAAS,MAAO,SAAS,EAAE,OAAO,CAAC;AAAA,QACjE;AACA,cAAM,eAAe,SAAS;AAAA,UAAO,OACnC,EAAE,oBAAoB,KAAK,OAAK,EAAE,YAAY,MAAM;AAAA,QACtD;AACA,eAAO,aAAa,SAAS,IAAI,eAAe;AAAA,MAClD,GAAG,CAAC,UAAU,QAAQ,CAAC;AAEvB,YAAM,EAAE,OAAO,OAAO,SAAS,aAAa,IAAI,SAAS,YAAY;AACnE,cAAM,YAAY,UAAU,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAC5D,cAAM,UAAU,UAAU,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACxD,eAAO,IAAI,SAAS,WAAW,OAAO;AAAA,MACxC,GAAG,CAAC,KAAK,SAAS,CAAC;AAGnB,YAAM,gBAAgB,YAAY,OAAO,WAAmB;AAC1D,YAAI,eAAe,MAAM,MAAM,UAAa,iBAAiB,MAAM,EAAG;AACtE,4BAAoB,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE;AACzD,YAAI;AACF,gBAAM,YAAY,UAAU,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAC5D,gBAAM,UAAU,UAAU,IAAI,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACxD,gBAAM,OAAO,MAAM,IAAI,aAAa,QAAQ,WAAW,OAAO;AAC9D,4BAAkB,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE;AAAA,QACzD,QAAQ;AACN,4BAAkB,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE;AAAA,QACzD,UAAE;AACA,8BAAoB,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE;AAAA,QAC5D;AAAA,MACF,GAAG,CAAC,KAAK,WAAW,gBAAgB,gBAAgB,CAAC;AAIrD,YAAM,gBAAgB,QAAQ,MAAM;AAClC,YAAI,CAAC,WAAW,OAAQ,QAAO,CAAC;AAChC,cAAM,aAAa,UAAU;AAC7B,cAAM,aAAa,OAAO,QAAQ,OAAK,EAAE,UAAU,CAAC,CAAC;AACrD,cAAM,qBAAqB,cAAc,WAAW,SAAS;AAC7D,cAAM,qBAAqB,cAAc,WAAW,SAAS;AAC7D,YAAI,CAAC,sBAAsB,CAAC,mBAAoB,QAAO;AACvD,cAAM,UAAU,oBAAI,IAAI;AAAA,UACtB,GAAI,qBAAqB,aAAc,UAAU,IAAI,OAAK,EAAE,UAAU;AAAA,UACtE,GAAI,qBAAqB,aAAc,CAAC;AAAA,QAC1C,CAAC;AACD,eAAO,UAAU,OAAO,OAAK,QAAQ,IAAI,EAAE,UAAU,CAAC;AAAA,MACxD,GAAG,CAAC,WAAW,UAAU,KAAK,CAAC;AAE/B,YAAM,oBAAoB;AAAA,QACxB,OAAO,YAA8D;AACnE,gBAAM,WAAW,MAAM,IAAI,YAAY,OAAO;AAC9C,sBAAY,EAAE,SAAS,8BAA8B,UAAU,UAAU,CAAC;AAC1E,sBAAY;AACZ,iBAAO;AAAA,QACT;AAAA,QACA,CAAC,KAAK,WAAW;AAAA,MACnB;AAEA,YAAM,kBAAkB;AAAA,QACtB,OAAO,UAAkB;AACvB,cAAI;AACF,kBAAM,IAAI,UAAU,KAAK;AACzB,wBAAY,EAAE,SAAS,4BAA4B,UAAU,UAAU,CAAC;AACxE,wBAAY;AAAA,UACd,SAAS,GAAQ;AACf,wBAAY,EAAE,SAAS,yBAAyB,EAAE,OAAO,IAAI,UAAU,QAAQ,CAAC;AAAA,UAClF;AAAA,QACF;AAAA,QACA,CAAC,KAAK,WAAW;AAAA,MACnB;AAEA,YAAM,mBAAmB,eAAe,CAAC;AAEzC,UAAI,kBAAkB;AACpB,eACE,gBAAAD,OAAA,cAACG,MAAA,EAAI,SAAQ,QAAO,gBAAe,UAAS,YAAW,UAAS,WAAU,UACxE,gBAAAH,OAAA,cAACI,mBAAA,IAAiB,CACpB;AAAA,MAEJ;AAGA,UAAI,aAAa,CAAC,UAAU;AAC1B,cAAM,wBAAyB,WAAmB,MAAM,iBAAiB;AACzE,cAAM,OAAQ,WAAmB,MAAM;AACvC,eACE,gBAAAJ,OAAA,cAACG,MAAA,EAAI,GAAG,KACN,gBAAAH,OAAA,cAACM,QAAA,EAAM,IAAI,EAAE,GAAG,EAAE,KAChB,gBAAAN,OAAA,cAACK,aAAA,EAAW,SAAQ,MAAK,cAAY,QAAC,yBAAuB,GAC7D,gBAAAL,OAAA,cAACK,aAAA,EAAW,OAAM,kBAAiB,WAAS,QAAC,yDAE7C,GACC,OACC,gBAAAL,OAAA,cAACK,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAAkB,IAAK,IAEzD,gBAAAL,OAAA,cAACK,aAAA,EAAW,SAAQ,SAAQ,OAAM,oBAC/B,wBACG,qEACA,kJACN,CAEJ,CACF;AAAA,MAEJ;AAEA,aACE,gBAAAL,OAAA,cAACG,MAAA,EAAI,GAAG,KACN,gBAAAH,OAAA,cAACE,OAAA,EAAK,WAAS,MAAC,SAAS,KACvB,gBAAAF,OAAA,cAACE,OAAA,EAAK,MAAI,MAAC,IAAI,MACb,gBAAAF,OAAA,cAAC,mBAAgB,UAAoB,OAAO,SAAS,CAAC,GAAG,SAAS,eAAe,cAAc,CACjG,GAEA,gBAAAA,OAAA,cAACE,OAAA,EAAK,MAAI,MAAC,IAAI,MACb,gBAAAF,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS,CAAC;AAAA,UACjB,SAAS;AAAA,UACT;AAAA,UACA,cAAc,YAAU;AACtB,gBAAI,eAAe,MAAM,MAAM,OAAW,eAAc,MAAM;AAC9D,mBAAO,eAAe,MAAM,KAAK;AAAA,UACnC;AAAA,UACA,qBAAqB,YAAU,iBAAiB,MAAM,KAAK;AAAA;AAAA,MAC7D,CACF,GAEA,gBAAAA,OAAA,cAACE,OAAA,EAAK,MAAI,MAAC,IAAI,MACb,gBAAAF,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,QAAQ,CAAC;AAAA,UACf,QAAQ;AAAA,UACR,OAAO,SAAS,CAAC;AAAA,UACjB,SAAS,eAAe;AAAA,UACxB,eAAe;AAAA,UACf,aAAa;AAAA;AAAA,MACf,CACF,GAEA,gBAAAA,OAAA,cAACE,OAAA,EAAK,MAAI,MAAC,IAAI,MACb,gBAAAF,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,SAAS;AAAA,UAChB,QAAQ,aAAa,CAAC;AAAA,UACtB;AAAA,UACA,mBAAmB;AAAA,UACnB,SAAS;AAAA;AAAA,MACX,CACF,CACF,GAEA,gBAAAA,OAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,CAAC,CAAC;AAAA,UACR,kBAAkB;AAAA,UAClB,SAAS,MAAM,YAAY,IAAI;AAAA,UAC/B,cAAc,EAAE,UAAU,UAAU,YAAY,QAAQ;AAAA;AAAA,QAEvD,WACC,gBAAAA,OAAA,cAAC,SAAM,UAAU,SAAS,UAAU,SAAS,MAAM,YAAY,IAAI,KAChE,SAAS,OACZ,IACE;AAAA,MACN,CACF;AAAA,IAEJ;AAAA;AAAA;;;AClNA;AAVA,OAAOO,YAAW;AAClB,SAAS,cAAc,sBAAsB;AAC7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGA,IAAM,gBAAgB,qBAAqB;AAAA,EAChD,IAAI;AAAA,EACJ,YAAY;AAAA,IACV,mBAAmB;AAAA,MACjB,SAAS,iBAAiB;AAAA,QACxB,KAAK;AAAA,QACL,MAAM,EAAE,UAAU,YAAY;AAAA,QAC9B,SAAS,CAAC,EAAE,SAAS,MAAM,IAAI,WAAW,QAAQ;AAAA,MACpD,CAAC;AAAA,IACH,CAAC;AAAA,IACD,uBAAuB;AAAA,MACrB,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM,gBAAAA,OAAA,cAAC,oBAAe;AAAA,MACtB,aAAa;AAAA,IACf,CAAC;AAAA,IACD,oBAAoB;AAAA,MAClB,aAAa;AAAA,MACb,QAAQ,YAAY;AAClB,cAAM,EAAE,aAAAC,aAAY,IAAI,MAAM;AAC9B,eAAOD,OAAM,cAAcC,YAAW;AAAA,MACxC;AAAA,IACF,CAAC;AAAA,EACH;AACF,CAAC;;;ACnCD;AACA;AACA;AACA;AACA;AACA;",
|
|
4
|
+
"sourcesContent": ["import { createApiRef, FetchApi } from '@backstage/core-plugin-api';\nimport {\n UserInfo,\n VirtualKey,\n ModelInfo,\n UsageMetrics,\n TeamInfo,\n GenerateKeyRequest,\n GenerateKeyResponse,\n UpdateKeyRequest,\n} from './types';\n\nclass ApiError extends Error {\n body: unknown;\n status: number;\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface LiteLlmApiInterface {\n getUserInfo(): Promise<UserInfo>;\n listKeys(): Promise<VirtualKey[]>;\n generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse>;\n updateKey(keyId: string, request: UpdateKeyRequest): Promise<VirtualKey>;\n deleteKey(keyId: string): Promise<{ success: boolean }>;\n listModels(): Promise<ModelInfo[]>;\n getTeams(): Promise<TeamInfo[]>;\n getUsage(startDate: string, endDate: string): Promise<UsageMetrics>;\n getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics>;\n}\n\nexport const liteLlmApiRef = createApiRef<LiteLlmApiInterface>({\n id: 'plugin.litellm.api',\n});\n\nexport class LiteLlmApi implements LiteLlmApiInterface {\n private fetchApi: FetchApi;\n private basePath: string;\n\n constructor(fetchApi: FetchApi, basePath: string = '/api/litellm') {\n this.fetchApi = fetchApi;\n this.basePath = basePath;\n }\n\n private async throwIfNotOk(response: Response): Promise<void> {\n if (!response.ok) {\n let body: unknown;\n try { body = await response.json(); } catch { body = await response.text().catch(() => ''); }\n throw new ApiError(`${response.status} ${response.statusText}`, response.status, body);\n }\n }\n\n private async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.basePath}${path}`, window.location.origin);\n if (params) {\n Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value));\n }\n const response = await this.fetchApi.fetch(url.toString());\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async del<T>(path: string): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n // User identity is resolved server-side from the Backstage Bearer token.\n // No user_id param needed on the frontend.\n async getUserInfo(): Promise<UserInfo> {\n return this.get<UserInfo>('/user/info');\n }\n\n async listKeys(): Promise<VirtualKey[]> {\n return this.get<VirtualKey[]>('/keys');\n }\n\n async generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse> {\n return this.post<GenerateKeyResponse>('/keys/generate', request);\n }\n\n async updateKey(keyId: string, request: UpdateKeyRequest): Promise<VirtualKey> {\n return this.post<VirtualKey>(`/keys/${encodeURIComponent(keyId)}/update`, request);\n }\n\n async deleteKey(keyId: string): Promise<{ success: boolean }> {\n return this.del<{ success: boolean }>(`/keys/${encodeURIComponent(keyId)}`);\n }\n\n async listModels(): Promise<ModelInfo[]> {\n return this.get<ModelInfo[]>('/models');\n }\n\n async getTeams(): Promise<TeamInfo[]> {\n return this.get<TeamInfo[]>('/teams');\n }\n\n async getUsage(startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>('/usage', { start_date: startDate, end_date: endDate });\n }\n\n async getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>(`/teams/${encodeURIComponent(teamId)}/usage`, {\n start_date: startDate,\n end_date: endDate,\n });\n }\n}\n", "import React from 'react';\nimport { Box, Typography, LinearProgress, Paper, Chip } from '@mui/material';\nimport { Warning } from '@mui/icons-material';\nimport { UserInfo, TeamInfo } from '../types';\n\ninterface DashboardHeaderProps {\n userInfo: UserInfo;\n teams: TeamInfo[];\n loading: boolean;\n}\n\nexport const DashboardHeader: React.FC<DashboardHeaderProps> = ({ userInfo, teams, loading }) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;\n const budget = userInfo.max_budget ?? 0;\n const spend = userInfo.spend ?? userInfo.current_spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"flex-start\" gap={2} flexWrap=\"wrap\">\n <Box flexGrow={1}>\n <Typography variant=\"h6\">{displayName}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {userInfo.user_id}\n </Typography>\n\n {teams.length > 0 && (\n <Box display=\"flex\" gap={0.75} flexWrap=\"wrap\" mt={1}>\n {teams.map(team => (\n <Chip\n key={team.team_id}\n label={team.team_alias || team.team_id}\n size=\"small\"\n variant=\"outlined\"\n color=\"primary\"\n />\n ))}\n </Box>\n )}\n </Box>\n\n {budget > 0 && (\n <Box minWidth={220}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip icon={<Warning />} label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 8, borderRadius: 1 }}\n />\n </Box>\n )}\n </Box>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Button,\n IconButton,\n Box,\n Typography,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n MenuItem,\n Chip,\n CircularProgress,\n Autocomplete,\n} from '@mui/material';\nimport { ContentCopy, Delete, Add, Edit, Visibility, VisibilityOff } from '@mui/icons-material';\nimport {\n VirtualKey,\n ModelInfo,\n TeamInfo,\n GenerateKeyRequest,\n GenerateKeyResponse,\n UpdateKeyRequest,\n} from '../types';\n\ninterface KeysTableProps {\n keys: VirtualKey[];\n models: ModelInfo[];\n teams: TeamInfo[];\n loading: boolean;\n onGenerateKey: (request: GenerateKeyRequest) => Promise<GenerateKeyResponse>;\n onUpdateKey: (keyId: string, request: UpdateKeyRequest) => Promise<void>;\n onDeleteKey: (keyId: string) => Promise<void>;\n}\n\nconst KEY_TYPES: { value: string; label: string }[] = [\n { value: 'llm_api', label: 'AI API' },\n { value: 'management', label: 'Management' },\n { value: 'read_only', label: 'Read Only' },\n];\n\nconst maskKey = (key: string): string => {\n if (key.length <= 8) return '***';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n};\n\nconst formatDate = (dateStr: string): string => {\n try {\n return new Date(dateStr).toLocaleDateString();\n } catch {\n return dateStr;\n }\n};\n\nconst emptyForm = (): GenerateKeyRequest => ({\n alias: '',\n models: [],\n duration: '30d',\n max_budget: undefined,\n tpm_limit: undefined,\n team_id: undefined,\n key_type: 'llm_api',\n});\n\nconst keyToEditForm = (k: VirtualKey): UpdateKeyRequest => ({\n key_alias: k.key_alias ?? '',\n models: k.models ?? [],\n max_budget: k.max_budget,\n tpm_limit: k.tpm_limit,\n rpm_limit: k.rpm_limit,\n});\n\nexport const KeysTable: React.FC<KeysTableProps> = ({\n keys,\n models,\n teams,\n loading,\n onGenerateKey,\n onUpdateKey,\n onDeleteKey,\n}) => {\n const [generateModalOpen, setGenerateModalOpen] = useState(false);\n const [showKeyValue, setShowKeyValue] = useState<string | null>(null);\n const [newKeyValue, setNewKeyValue] = useState<string | null>(null);\n const [formData, setFormData] = useState<GenerateKeyRequest>(emptyForm());\n const [submitting, setSubmitting] = useState(false);\n\n const [editingKey, setEditingKey] = useState<VirtualKey | null>(null);\n const [editForm, setEditForm] = useState<UpdateKeyRequest>({});\n const [editSubmitting, setEditSubmitting] = useState(false);\n\n const selectedModels = models.filter(m => (formData.models || []).includes(m.model_name));\n const selectedTeam = teams.find(t => t.team_id === formData.team_id) ?? null;\n\n const editSelectedModels = models.filter(m => (editForm.models || []).includes(m.model_name));\n\n const handleGenerate = async () => {\n setSubmitting(true);\n try {\n const response = await onGenerateKey(formData);\n setNewKeyValue(response.key);\n setFormData(emptyForm());\n } catch (error) {\n console.error('Failed to generate key:', error);\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleCloseModal = () => {\n setGenerateModalOpen(false);\n setNewKeyValue(null);\n setFormData(emptyForm());\n };\n\n const handleOpenEdit = (k: VirtualKey) => {\n setEditingKey(k);\n setEditForm(keyToEditForm(k));\n };\n\n const handleCloseEdit = () => {\n setEditingKey(null);\n setEditForm({});\n };\n\n const handleUpdate = async () => {\n if (!editingKey) return;\n setEditSubmitting(true);\n try {\n await onUpdateKey(editingKey.key, editForm);\n handleCloseEdit();\n } catch (error) {\n console.error('Failed to update key:', error);\n } finally {\n setEditSubmitting(false);\n }\n };\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text);\n };\n\n return (\n <>\n <Paper sx={{ mb: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" p={2}>\n <Typography variant=\"h6\">Virtual Keys</Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<Add />}\n onClick={() => setGenerateModalOpen(true)}\n >\n Generate New Key\n </Button>\n </Box>\n\n <TableContainer>\n <Table>\n <TableHead>\n <TableRow>\n <TableCell>Alias</TableCell>\n <TableCell>Key</TableCell>\n <TableCell>Created</TableCell>\n <TableCell>Spend</TableCell>\n <TableCell>Budget</TableCell>\n <TableCell>TPM Limit</TableCell>\n <TableCell>Models</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <CircularProgress size={24} />\n </TableCell>\n </TableRow>\n ) : keys.length === 0 ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <Typography color=\"text.secondary\">No keys found</Typography>\n </TableCell>\n </TableRow>\n ) : (\n keys.map((key) => (\n <TableRow key={key.key}>\n <TableCell>{key.key_alias || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" alignItems=\"center\" gap={0.5}>\n <Typography variant=\"body2\" component=\"code\" sx={{ fontFamily: 'monospace' }}>\n {showKeyValue === key.key ? key.key : maskKey(key.key)}\n </Typography>\n <IconButton\n size=\"small\"\n onClick={() => setShowKeyValue(showKeyValue === key.key ? null : key.key)}\n >\n {showKeyValue === key.key ? <VisibilityOff /> : <Visibility />}\n </IconButton>\n <IconButton size=\"small\" onClick={() => copyToClipboard(key.key)}>\n <ContentCopy fontSize=\"small\" />\n </IconButton>\n </Box>\n </TableCell>\n <TableCell>{formatDate(key.created_at)}</TableCell>\n <TableCell>${key.spend?.toFixed(4) || '0.00'}</TableCell>\n <TableCell>{key.max_budget ? `$${key.max_budget}` : '-'}</TableCell>\n <TableCell>{key.tpm_limit || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\">\n {key.models?.slice(0, 2).map((model) => (\n <Chip key={model} label={model} size=\"small\" />\n ))}\n {(key.models?.length || 0) > 2 && (\n <Chip label={`+${(key.models?.length || 0) - 2}`} size=\"small\" variant=\"outlined\" />\n )}\n </Box>\n </TableCell>\n <TableCell align=\"right\">\n <IconButton onClick={() => handleOpenEdit(key)}>\n <Edit fontSize=\"small\" />\n </IconButton>\n <IconButton color=\"error\" onClick={() => onDeleteKey(key.key)}>\n <Delete />\n </IconButton>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </TableContainer>\n </Paper>\n\n <Dialog open={generateModalOpen} onClose={handleCloseModal} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{newKeyValue ? 'Key Generated' : 'Generate New Key'}</DialogTitle>\n <DialogContent>\n {newKeyValue ? (\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n Copy this key now. You won't be able to see it again.\n </Typography>\n <Box\n display=\"flex\"\n alignItems=\"center\"\n gap={1}\n mt={2}\n p={2}\n sx={{ backgroundColor: 'action.hover', borderRadius: 1 }}\n >\n <Typography\n component=\"code\"\n sx={{ fontFamily: 'monospace', wordBreak: 'break-all', flex: 1 }}\n >\n {newKeyValue}\n </Typography>\n <IconButton onClick={() => copyToClipboard(newKeyValue)}>\n <ContentCopy />\n </IconButton>\n </Box>\n </Box>\n ) : (\n <Box display=\"flex\" flexDirection=\"column\" gap={2} mt={1}>\n <TextField\n label=\"Alias\"\n value={formData.alias || ''}\n onChange={(e) => setFormData({ ...formData, alias: e.target.value })}\n fullWidth\n />\n <TextField\n select\n label=\"Key Type\"\n value={formData.key_type || 'llm_api'}\n onChange={(e) => setFormData({ ...formData, key_type: e.target.value })}\n fullWidth\n >\n {KEY_TYPES.map(kt => (\n <MenuItem key={kt.value} value={kt.value}>{kt.label}</MenuItem>\n ))}\n </TextField>\n <TextField\n select\n label=\"Duration\"\n value={formData.duration || '30d'}\n onChange={(e) => setFormData({ ...formData, duration: e.target.value })}\n fullWidth\n >\n <MenuItem value=\"1d\">1 Day</MenuItem>\n <MenuItem value=\"7d\">7 Days</MenuItem>\n <MenuItem value=\"30d\">30 Days</MenuItem>\n <MenuItem value=\"90d\">90 Days</MenuItem>\n <MenuItem value=\"1y\">1 Year</MenuItem>\n </TextField>\n\n {teams.length > 0 && (\n <Autocomplete\n options={teams}\n getOptionLabel={t => t.team_alias || t.team_id}\n value={selectedTeam}\n onChange={(_e, team) =>\n setFormData({ ...formData, team_id: team?.team_id })\n }\n renderInput={params => (\n <TextField {...params} label=\"Team (optional)\" fullWidth />\n )}\n />\n )}\n\n {models.length > 0 && (\n <Autocomplete\n multiple\n options={models}\n groupBy={m => m.mode || 'other'}\n getOptionLabel={m => m.model_name}\n value={selectedModels}\n onChange={(_e, selected) =>\n setFormData({ ...formData, models: selected.map(m => m.model_name) })\n }\n renderOption={(props, m) => (\n <li {...props}>\n {m.model_name}\n {m.supports_function_calling && ' \uD83D\uDD27'}\n {m.supports_vision && ' \uD83D\uDC41\uFE0F'}\n </li>\n )}\n renderInput={params => (\n <TextField {...params} label=\"Models\" fullWidth />\n )}\n />\n )}\n\n <TextField\n label=\"Max Budget (USD)\"\n type=\"number\"\n value={formData.max_budget ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"TPM Limit\"\n type=\"number\"\n value={formData.tpm_limit ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseModal}>{newKeyValue ? 'Done' : 'Cancel'}</Button>\n {!newKeyValue && (\n <Button onClick={handleGenerate} variant=\"contained\" color=\"primary\" disabled={submitting}>\n {submitting ? <CircularProgress size={24} /> : 'Generate'}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n\n <Dialog open={!!editingKey} onClose={handleCloseEdit} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Edit Key</DialogTitle>\n <DialogContent>\n {editingKey && (\n <Box display=\"flex\" flexDirection=\"column\" gap={2} mt={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n <code style={{ fontFamily: 'monospace' }}>{maskKey(editingKey.key)}</code>\n </Typography>\n <TextField\n label=\"Alias\"\n value={editForm.key_alias || ''}\n onChange={(e) => setEditForm({ ...editForm, key_alias: e.target.value })}\n fullWidth\n />\n\n {models.length > 0 && (\n <Autocomplete\n multiple\n options={models}\n groupBy={m => m.mode || 'other'}\n getOptionLabel={m => m.model_name}\n value={editSelectedModels}\n onChange={(_e, selected) =>\n setEditForm({ ...editForm, models: selected.map(m => m.model_name) })\n }\n renderInput={params => (\n <TextField {...params} label=\"Models\" fullWidth />\n )}\n />\n )}\n\n <TextField\n label=\"Max Budget (USD)\"\n type=\"number\"\n value={editForm.max_budget ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"TPM Limit\"\n type=\"number\"\n value={editForm.tpm_limit ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"RPM Limit\"\n type=\"number\"\n value={editForm.rpm_limit ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseEdit}>Cancel</Button>\n <Button onClick={handleUpdate} variant=\"contained\" color=\"primary\" disabled={editSubmitting}>\n {editSubmitting ? <CircularProgress size={24} /> : 'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n CircularProgress,\n} from '@mui/material';\nimport {\n AreaChart,\n Area,\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { DateRange, UsageMetrics, ModelInfo } from '../types';\n\ninterface UsageStatsProps {\n usage: UsageMetrics | null;\n models: ModelInfo[];\n dateRange: DateRange;\n onDateRangeChange: (range: DateRange) => void;\n loading: boolean;\n}\n\ntype DatePreset = 'today' | '7d' | '30d';\n\nexport const UsageStats: React.FC<UsageStatsProps> = ({\n usage,\n models,\n dateRange,\n onDateRangeChange,\n loading,\n}) => {\n const [selectedPreset, setSelectedPreset] = useState<DatePreset>('7d');\n const [selectedModel, setSelectedModel] = useState<string>('all');\n\n const handlePresetChange = (preset: DatePreset) => {\n setSelectedPreset(preset);\n const end = new Date();\n const start = new Date();\n if (preset === 'today') start.setHours(0, 0, 0, 0);\n else if (preset === '7d') start.setDate(start.getDate() - 7);\n else if (preset === '30d') start.setDate(start.getDate() - 30);\n onDateRangeChange({ start, end });\n };\n\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"center\" p={4}>\n <CircularProgress />\n </Box>\n </Paper>\n );\n }\n\n const dailyData =\n usage?.daily_usage?.map((d) => ({\n date: d.date,\n spend: d.spend,\n promptTokens: d.prompt_tokens,\n completionTokens: d.completion_tokens,\n totalTokens: d.total_tokens,\n })) || [];\n\n const usageByModelData = Object.entries(usage?.usage_by_model || {}).map(([model, data]) => ({\n model,\n spend: data.total_spend,\n promptTokens: data.prompt_tokens,\n completionTokens: data.completion_tokens,\n }));\n\n const filteredModelData =\n selectedModel === 'all'\n ? usageByModelData\n : usageByModelData.filter((d) => d.model === selectedModel);\n\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={3}>\n <Typography variant=\"h6\">Usage Analytics</Typography>\n <Box display=\"flex\" gap={2} alignItems=\"center\">\n <FormControl size=\"small\" sx={{ minWidth: 140 }}>\n <InputLabel>Period</InputLabel>\n <Select\n value={selectedPreset}\n label=\"Period\"\n onChange={(e) => handlePresetChange(e.target.value as DatePreset)}\n >\n <MenuItem value=\"today\">Today</MenuItem>\n <MenuItem value=\"7d\">Last 7 days</MenuItem>\n <MenuItem value=\"30d\">Last 30 days</MenuItem>\n </Select>\n </FormControl>\n <FormControl size=\"small\" sx={{ minWidth: 150 }}>\n <InputLabel>Model</InputLabel>\n <Select\n value={selectedModel}\n label=\"Model\"\n onChange={(e) => setSelectedModel(e.target.value)}\n >\n <MenuItem value=\"all\">All Models</MenuItem>\n {models.map((m) => (\n <MenuItem key={m.model_name} value={m.model_name}>\n {m.model_name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Box>\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 12 }} />\n <YAxis tick={{ fontSize: 12 }} tickFormatter={(v) => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(value: number) => [`$${value.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Token Usage by Model\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={filteredModelData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"model\" tick={{ fontSize: 10 }} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"promptTokens\" name=\"Prompt Tokens\" fill=\"#8884d8\" stackId=\"a\" />\n <Bar dataKey=\"completionTokens\" name=\"Completion Tokens\" fill=\"#82ca9d\" stackId=\"a\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12}>\n <Box display=\"flex\" gap={4} flexWrap=\"wrap\">\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Spend</Typography>\n <Typography variant=\"h5\">${usage?.total_spend?.toFixed(4) || '0.00'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Tokens</Typography>\n <Typography variant=\"h5\">{usage?.total_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Prompt Tokens</Typography>\n <Typography variant=\"h5\">{usage?.prompt_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Completion Tokens</Typography>\n <Typography variant=\"h5\">{usage?.completion_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n </Box>\n </Grid>\n </Grid>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n LinearProgress,\n Chip,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TableContainer,\n Collapse,\n IconButton,\n CircularProgress,\n} from '@mui/material';\nimport { ExpandMore, ExpandLess, Group } from '@mui/icons-material';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n} from 'recharts';\nimport { TeamInfo, UsageMetrics, DateRange } from '../types';\n\ninterface TeamCardProps {\n team: TeamInfo;\n usage: UsageMetrics | null;\n usageLoading: boolean;\n}\n\nconst TeamCard: React.FC<TeamCardProps> = ({ team, usage, usageLoading }) => {\n const [expanded, setExpanded] = useState(false);\n\n const budget = team.max_budget ?? 0;\n const spend = team.spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n const dailyData = usage?.daily_usage?.map(d => ({ date: d.date, spend: d.spend })) ?? [];\n\n return (\n <Paper variant=\"outlined\" sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"action\" />\n <Box flexGrow={1}>\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {team.team_alias ?? team.team_id}\n </Typography>\n {team.models?.length ? (\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\" mt={0.5}>\n {team.models.map(m => (\n <Chip key={m} label={m} size=\"small\" variant=\"outlined\" />\n ))}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">All models</Typography>\n )}\n </Box>\n <IconButton size=\"small\" onClick={() => setExpanded(e => !e)}>\n {expanded ? <ExpandLess /> : <ExpandMore />}\n </IconButton>\n </Box>\n\n {budget > 0 && (\n <Box mt={1.5}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 6, borderRadius: 1 }}\n />\n </Box>\n )}\n\n <Collapse in={expanded}>\n <Box mt={2}>\n {usageLoading ? (\n <Box display=\"flex\" justifyContent=\"center\" p={2}>\n <CircularProgress size={24} />\n </Box>\n ) : dailyData.length > 0 ? (\n <>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={160}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} tickFormatter={v => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(v: number) => [`$${v.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </>\n ) : null}\n\n {team.members_with_roles?.length ? (\n <Box mt={2}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Members\n </Typography>\n <TableContainer>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>User</TableCell>\n <TableCell>Role</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {team.members_with_roles.map(m => (\n <TableRow key={m.user_id}>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: 12 }}>{m.user_id}</TableCell>\n <TableCell>\n <Chip\n label={m.role}\n size=\"small\"\n color={m.role === 'admin' ? 'primary' : 'default'}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n ) : null}\n </Box>\n </Collapse>\n </Paper>\n );\n};\n\ninterface TeamUsageProps {\n teams: TeamInfo[];\n loading: boolean;\n dateRange: DateRange;\n getTeamUsage: (teamId: string) => UsageMetrics | null;\n getTeamUsageLoading: (teamId: string) => boolean;\n}\n\nexport const TeamUsage: React.FC<TeamUsageProps> = ({\n teams,\n loading,\n getTeamUsage,\n getTeamUsageLoading,\n}) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n if (!teams.length) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"disabled\" />\n <Typography color=\"text.secondary\" variant=\"body2\">\n No team membership found in LiteLLM for this account.\n </Typography>\n </Box>\n </Paper>\n );\n }\n\n return (\n <Box>\n <Typography variant=\"h6\" mb={1}>Teams</Typography>\n {teams.map(team => (\n <TeamCard\n key={team.team_id}\n team={team}\n usage={getTeamUsage(team.team_id)}\n usageLoading={getTeamUsageLoading(team.team_id)}\n />\n ))}\n </Box>\n );\n};\n", "import React, { useState, useCallback, useMemo } from 'react';\nimport { Grid, Box, Snackbar, Alert, CircularProgress, Typography, Paper } from '@mui/material';\nimport { useAsync, useAsyncRetry } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { DashboardHeader } from './DashboardHeader';\nimport { KeysTable } from './KeysTable';\nimport { UsageStats } from './UsageStats';\nimport { TeamUsage } from './TeamUsage';\nimport { liteLlmApiRef } from '../api';\nimport { DateRange, GenerateKeyRequest, GenerateKeyResponse, UpdateKeyRequest, UsageMetrics } from '../types';\n\nexport const LiteLLMPage: React.FC = () => {\n const api = useApi(liteLlmApiRef);\n\n const [dateRange, setDateRange] = useState<DateRange>(() => {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 7);\n return { start, end };\n });\n\n const [snackbar, setSnackbar] = useState<{ message: string; severity: 'success' | 'error' } | null>(null);\n\n // Team usage cache: teamId -> UsageMetrics\n const [teamUsageCache, setTeamUsageCache] = useState<Record<string, UsageMetrics | null>>({});\n const [teamUsageLoading, setTeamUsageLoading] = useState<Record<string, boolean>>({});\n\n const { value: userInfo, loading: userLoading, error: userError } = useAsync(\n () => api.getUserInfo(),\n [api],\n );\n\n const { value: keys, loading: keysLoading, retry: refreshKeys } = useAsyncRetry(\n async () => {\n try {\n return await api.listKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to load keys: ${e.message}`, severity: 'error' });\n return [];\n }\n },\n [api],\n );\n\n const { value: allModels, loading: modelsLoading } = useAsync(\n () => api.listModels().catch(() => []),\n [api],\n );\n\n const { value: allTeams, loading: teamsLoading } = useAsync(\n () => api.getTeams().catch(() => []),\n [api],\n );\n\n // Filter to teams the current user belongs to\n const teams = useMemo(() => {\n if (!allTeams?.length) return [];\n if (!userInfo) return allTeams;\n const userId = userInfo.user_id;\n if (userInfo.teams?.length) {\n return allTeams.filter(t => userInfo.teams!.includes(t.team_id));\n }\n const byMembership = allTeams.filter(t =>\n t.members_with_roles?.some(m => m.user_id === userId),\n );\n return byMembership.length > 0 ? byMembership : allTeams;\n }, [allTeams, userInfo]);\n\n const { value: usage, loading: usageLoading } = useAsync(async () => {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n return api.getUsage(startDate, endDate);\n }, [api, dateRange]);\n\n // Fetch team usage on demand when a team card is expanded\n const loadTeamUsage = useCallback(async (teamId: string) => {\n if (teamUsageCache[teamId] !== undefined || teamUsageLoading[teamId]) return;\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: true }));\n try {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n const data = await api.getTeamUsage(teamId, startDate, endDate);\n setTeamUsageCache(prev => ({ ...prev, [teamId]: data }));\n } catch {\n setTeamUsageCache(prev => ({ ...prev, [teamId]: null }));\n } finally {\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: false }));\n }\n }, [api, dateRange, teamUsageCache, teamUsageLoading]);\n\n // Models available for key generation: intersection of all models with user-level\n // and team-level restrictions. If a user has no model restrictions, all models are allowed.\n const allowedModels = useMemo(() => {\n if (!allModels?.length) return [];\n const userModels = userInfo?.models;\n const teamModels = teams?.flatMap(t => t.models ?? []);\n const hasUserRestriction = userModels && userModels.length > 0;\n const hasTeamRestriction = teamModels && teamModels.length > 0;\n if (!hasUserRestriction && !hasTeamRestriction) return allModels;\n const allowed = new Set([\n ...(hasUserRestriction ? userModels! : allModels.map(m => m.model_name)),\n ...(hasTeamRestriction ? teamModels! : []),\n ]);\n return allModels.filter(m => allowed.has(m.model_name));\n }, [allModels, userInfo, teams]);\n\n const handleGenerateKey = useCallback(\n async (request: GenerateKeyRequest): Promise<GenerateKeyResponse> => {\n const response = await api.generateKey(request);\n setSnackbar({ message: 'Key generated successfully', severity: 'success' });\n refreshKeys();\n return response;\n },\n [api, refreshKeys],\n );\n\n const handleUpdateKey = useCallback(\n async (keyId: string, request: UpdateKeyRequest) => {\n try {\n await api.updateKey(keyId, request);\n setSnackbar({ message: 'Key updated successfully', severity: 'success' });\n refreshKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to update key: ${e.message}`, severity: 'error' });\n throw e;\n }\n },\n [api, refreshKeys],\n );\n\n const handleDeleteKey = useCallback(\n async (keyId: string) => {\n try {\n await api.deleteKey(keyId);\n setSnackbar({ message: 'Key revoked successfully', severity: 'success' });\n refreshKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to revoke key: ${e.message}`, severity: 'error' });\n }\n },\n [api, refreshKeys],\n );\n\n const isInitialLoading = userLoading && !userInfo;\n\n if (isInitialLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"50vh\">\n <CircularProgress />\n </Box>\n );\n }\n\n // User exists in Backstage but has no LiteLLM account\n if (userError || !userInfo) {\n const isProvisioningEnabled = (userError as any)?.body?.provisioning === true;\n const hint = (userError as any)?.body?.hint;\n return (\n <Box p={3}>\n <Paper sx={{ p: 3 }}>\n <Typography variant=\"h6\" gutterBottom>Account not provisioned</Typography>\n <Typography color=\"text.secondary\" paragraph>\n Your Backstage account is not linked to a LiteLLM user.\n </Typography>\n {hint ? (\n <Typography variant=\"body2\" color=\"text.secondary\">{hint}</Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {isProvisioningEnabled\n ? 'Auto-provisioning is enabled but failed. Check the backend logs.'\n : 'Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.'}\n </Typography>\n )}\n </Paper>\n </Box>\n );\n }\n\n return (\n <Box p={3}>\n <Grid container spacing={2}>\n <Grid item xs={12}>\n <DashboardHeader userInfo={userInfo} teams={teams ?? []} loading={userLoading || teamsLoading} />\n </Grid>\n\n <Grid item xs={12}>\n <TeamUsage\n teams={teams ?? []}\n loading={teamsLoading}\n dateRange={dateRange}\n getTeamUsage={teamId => {\n if (teamUsageCache[teamId] === undefined) loadTeamUsage(teamId);\n return teamUsageCache[teamId] ?? null;\n }}\n getTeamUsageLoading={teamId => teamUsageLoading[teamId] ?? false}\n />\n </Grid>\n\n <Grid item xs={12}>\n <KeysTable\n keys={keys ?? []}\n models={allowedModels}\n teams={teams ?? []}\n loading={keysLoading || modelsLoading}\n onGenerateKey={handleGenerateKey}\n onUpdateKey={handleUpdateKey}\n onDeleteKey={handleDeleteKey}\n />\n </Grid>\n\n <Grid item xs={12}>\n <UsageStats\n usage={usage ?? null}\n models={allModels ?? []}\n dateRange={dateRange}\n onDateRangeChange={setDateRange}\n loading={usageLoading}\n />\n </Grid>\n </Grid>\n\n <Snackbar\n open={!!snackbar}\n autoHideDuration={5000}\n onClose={() => setSnackbar(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n >\n {snackbar ? (\n <Alert severity={snackbar.severity} onClose={() => setSnackbar(null)}>\n {snackbar.message}\n </Alert>\n ) : undefined}\n </Snackbar>\n </Box>\n );\n};\n", "import React from 'react';\nimport { TrendingUp as TrendingUpIcon } from '@mui/icons-material';\nimport {\n createFrontendPlugin,\n createApiExtension,\n createPageExtension,\n createApiFactory,\n createSidebarExtension,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { liteLlmApiRef, LiteLlmApi } from './api';\n\nexport const litellmPlugin = createFrontendPlugin({\n id: 'litellm',\n extensions: [\n createApiExtension({\n factory: createApiFactory({\n api: liteLlmApiRef,\n deps: { fetchApi: fetchApiRef },\n factory: ({ fetchApi }) => new LiteLlmApi(fetchApi),\n }),\n }),\n createSidebarExtension({\n id: 'root',\n title: 'LiteLLM',\n icon: <TrendingUpIcon />,\n defaultPath: '/litellm',\n }),\n createPageExtension({\n defaultPath: '/litellm',\n loader: async () => {\n const { LiteLLMPage } = await import('./components/LiteLLMPage');\n return React.createElement(LiteLLMPage);\n },\n }),\n ],\n});\n", "export { litellmPlugin } from './plugin';\nexport { LiteLLMPage } from './components/LiteLLMPage';\nexport { DashboardHeader } from './components/DashboardHeader';\nexport { KeysTable } from './components/KeysTable';\nexport { UsageStats } from './components/UsageStats';\nexport { TeamUsage } from './components/TeamUsage';\nexport { LiteLlmApi, liteLlmApiRef } from './api';\nexport type { LiteLlmApiInterface } from './api';\nexport * from './types';\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["React", "Paper", "Box", "Typography", "Chip", "React", "useState", "Paper", "Box", "Typography", "MenuItem", "CircularProgress", "React", "useState", "Paper", "Box", "Typography", "LinearProgress", "Chip", "Table", "TableBody", "TableCell", "TableHead", "TableRow", "TableContainer", "IconButton", "CircularProgress", "AreaChart", "Area", "XAxis", "YAxis", "CartesianGrid", "Tooltip", "ResponsiveContainer", "React", "useState", "Grid", "Box", "CircularProgress", "Typography", "Paper", "React", "LiteLLMPage"]
|
|
7
7
|
}
|