@acarmisc/backstage-plugin-litellm 0.1.8 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -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
  }
@@ -140,7 +143,7 @@ var init_DashboardHeader = __esm({
140
143
  });
141
144
 
142
145
  // src/components/KeysTable.tsx
143
- import React2, { useState } from "react";
146
+ import React2, { useEffect, useState } from "react";
144
147
  import {
145
148
  Paper as Paper2,
146
149
  Table,
@@ -163,8 +166,8 @@ 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 maskKey, formatDate, emptyForm, keyToEditForm, KeysTable;
168
171
  var init_KeysTable = __esm({
169
172
  "src/components/KeysTable.tsx"() {
170
173
  "use strict";
@@ -185,7 +188,15 @@ var init_KeysTable = __esm({
185
188
  duration: "30d",
186
189
  max_budget: void 0,
187
190
  tpm_limit: void 0,
188
- team_id: void 0
191
+ team_id: void 0,
192
+ key_type: "llm_api"
193
+ });
194
+ keyToEditForm = (k) => ({
195
+ key_alias: k.key_alias ?? "",
196
+ models: k.models ?? [],
197
+ max_budget: k.max_budget,
198
+ tpm_limit: k.tpm_limit,
199
+ rpm_limit: k.rpm_limit
189
200
  });
190
201
  KeysTable = ({
191
202
  keys,
@@ -193,6 +204,7 @@ var init_KeysTable = __esm({
193
204
  teams,
194
205
  loading,
195
206
  onGenerateKey,
207
+ onUpdateKey,
196
208
  onDeleteKey
197
209
  }) => {
198
210
  const [generateModalOpen, setGenerateModalOpen] = useState(false);
@@ -200,8 +212,20 @@ var init_KeysTable = __esm({
200
212
  const [newKeyValue, setNewKeyValue] = useState(null);
201
213
  const [formData, setFormData] = useState(emptyForm());
202
214
  const [submitting, setSubmitting] = useState(false);
215
+ useEffect(() => {
216
+ if (!generateModalOpen) return;
217
+ if (!formData.team_id && teams.length > 0) {
218
+ setFormData((f) => ({ ...f, team_id: teams[0].team_id }));
219
+ }
220
+ }, [generateModalOpen, teams, formData.team_id]);
221
+ const teamRequired = teams.length > 0;
222
+ const canGenerate = !teamRequired || !!formData.team_id;
223
+ const [editingKey, setEditingKey] = useState(null);
224
+ const [editForm, setEditForm] = useState({});
225
+ const [editSubmitting, setEditSubmitting] = useState(false);
203
226
  const selectedModels = models.filter((m) => (formData.models || []).includes(m.model_name));
204
227
  const selectedTeam = teams.find((t) => t.team_id === formData.team_id) ?? null;
228
+ const editSelectedModels = models.filter((m) => (editForm.models || []).includes(m.model_name));
205
229
  const handleGenerate = async () => {
206
230
  setSubmitting(true);
207
231
  try {
@@ -219,6 +243,26 @@ var init_KeysTable = __esm({
219
243
  setNewKeyValue(null);
220
244
  setFormData(emptyForm());
221
245
  };
246
+ const handleOpenEdit = (k) => {
247
+ setEditingKey(k);
248
+ setEditForm(keyToEditForm(k));
249
+ };
250
+ const handleCloseEdit = () => {
251
+ setEditingKey(null);
252
+ setEditForm({});
253
+ };
254
+ const handleUpdate = async () => {
255
+ if (!editingKey) return;
256
+ setEditSubmitting(true);
257
+ try {
258
+ await onUpdateKey(editingKey.key, editForm);
259
+ handleCloseEdit();
260
+ } catch (error) {
261
+ console.error("Failed to update key:", error);
262
+ } finally {
263
+ setEditSubmitting(false);
264
+ }
265
+ };
222
266
  const copyToClipboard = (text) => {
223
267
  navigator.clipboard.writeText(text);
224
268
  };
@@ -238,7 +282,7 @@ var init_KeysTable = __esm({
238
282
  onClick: () => setShowKeyValue(showKeyValue === key.key ? null : key.key)
239
283
  },
240
284
  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(
285
+ ), /* @__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
286
  Box2,
243
287
  {
244
288
  display: "flex",
@@ -286,7 +330,17 @@ var init_KeysTable = __esm({
286
330
  getOptionLabel: (t) => t.team_alias || t.team_id,
287
331
  value: selectedTeam,
288
332
  onChange: (_e, team) => setFormData({ ...formData, team_id: team?.team_id }),
289
- renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Team (optional)", fullWidth: true })
333
+ renderInput: (params) => /* @__PURE__ */ React2.createElement(
334
+ TextField,
335
+ {
336
+ ...params,
337
+ label: "Team",
338
+ required: true,
339
+ error: teamRequired && !formData.team_id,
340
+ helperText: teamRequired && !formData.team_id ? "Pick a team to scope this key" : void 0,
341
+ fullWidth: true
342
+ }
343
+ )
290
344
  }
291
345
  ), models.length > 0 && /* @__PURE__ */ React2.createElement(
292
346
  Autocomplete,
@@ -318,13 +372,68 @@ var init_KeysTable = __esm({
318
372
  onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
319
373
  fullWidth: true
320
374
  }
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"))));
375
+ ))), /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal }, newKeyValue ? "Done" : "Cancel"), !newKeyValue && /* @__PURE__ */ React2.createElement(
376
+ Button,
377
+ {
378
+ onClick: handleGenerate,
379
+ variant: "contained",
380
+ color: "primary",
381
+ disabled: submitting || !canGenerate
382
+ },
383
+ submitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Generate"
384
+ ))), /* @__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(
385
+ TextField,
386
+ {
387
+ label: "Alias",
388
+ value: editForm.key_alias || "",
389
+ onChange: (e) => setEditForm({ ...editForm, key_alias: e.target.value }),
390
+ fullWidth: true
391
+ }
392
+ ), models.length > 0 && /* @__PURE__ */ React2.createElement(
393
+ Autocomplete,
394
+ {
395
+ multiple: true,
396
+ options: models,
397
+ groupBy: (m) => m.mode || "other",
398
+ getOptionLabel: (m) => m.model_name,
399
+ value: editSelectedModels,
400
+ onChange: (_e, selected) => setEditForm({ ...editForm, models: selected.map((m) => m.model_name) }),
401
+ renderInput: (params) => /* @__PURE__ */ React2.createElement(TextField, { ...params, label: "Models", fullWidth: true })
402
+ }
403
+ ), /* @__PURE__ */ React2.createElement(
404
+ TextField,
405
+ {
406
+ label: "Max Budget (USD)",
407
+ type: "number",
408
+ value: editForm.max_budget ?? "",
409
+ onChange: (e) => setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
410
+ fullWidth: true
411
+ }
412
+ ), /* @__PURE__ */ React2.createElement(
413
+ TextField,
414
+ {
415
+ label: "TPM Limit",
416
+ type: "number",
417
+ value: editForm.tpm_limit ?? "",
418
+ onChange: (e) => setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
419
+ fullWidth: true
420
+ }
421
+ ), /* @__PURE__ */ React2.createElement(
422
+ TextField,
423
+ {
424
+ label: "RPM Limit",
425
+ type: "number",
426
+ value: editForm.rpm_limit ?? "",
427
+ onChange: (e) => setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
428
+ fullWidth: true
429
+ }
430
+ ))), /* @__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
431
  };
323
432
  }
324
433
  });
325
434
 
326
435
  // src/components/UsageStats.tsx
327
- import React3, { useState as useState2 } from "react";
436
+ import React3, { useMemo, useState as useState2 } from "react";
328
437
  import {
329
438
  Paper as Paper3,
330
439
  Box as Box3,
@@ -334,7 +443,17 @@ import {
334
443
  Select,
335
444
  MenuItem as MenuItem2,
336
445
  Grid,
337
- CircularProgress as CircularProgress2
446
+ CircularProgress as CircularProgress2,
447
+ Tabs,
448
+ Tab,
449
+ Table as Table2,
450
+ TableBody as TableBody2,
451
+ TableCell as TableCell2,
452
+ TableContainer as TableContainer2,
453
+ TableHead as TableHead2,
454
+ TableRow as TableRow2,
455
+ Chip as Chip3,
456
+ LinearProgress as LinearProgress2
338
457
  } from "@mui/material";
339
458
  import {
340
459
  AreaChart,
@@ -348,10 +467,14 @@ import {
348
467
  ResponsiveContainer,
349
468
  Legend
350
469
  } from "recharts";
351
- var UsageStats;
470
+ var fmtUsd, fmtInt, fmtPct, KpiCard, UsageStats;
352
471
  var init_UsageStats = __esm({
353
472
  "src/components/UsageStats.tsx"() {
354
473
  "use strict";
474
+ fmtUsd = (n) => `$${(n ?? 0).toFixed(n < 1 ? 4 : 2)}`;
475
+ fmtInt = (n) => (n ?? 0).toLocaleString();
476
+ fmtPct = (n) => `${(n * 100).toFixed(1)}%`;
477
+ KpiCard = ({ label, value, hint }) => /* @__PURE__ */ React3.createElement(Paper3, { variant: "outlined", sx: { p: 2, height: "100%" } }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary" }, label), /* @__PURE__ */ React3.createElement(Typography3, { variant: "h5", sx: { mt: 0.5 } }, value), hint ? /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary" }, hint) : null);
355
478
  UsageStats = ({
356
479
  usage,
357
480
  models,
@@ -361,6 +484,7 @@ var init_UsageStats = __esm({
361
484
  }) => {
362
485
  const [selectedPreset, setSelectedPreset] = useState2("7d");
363
486
  const [selectedModel, setSelectedModel] = useState2("all");
487
+ const [tab, setTab] = useState2("costs");
364
488
  const handlePresetChange = (preset) => {
365
489
  setSelectedPreset(preset);
366
490
  const end = /* @__PURE__ */ new Date();
@@ -370,24 +494,71 @@ var init_UsageStats = __esm({
370
494
  else if (preset === "30d") start.setDate(start.getDate() - 30);
371
495
  onDateRangeChange({ start, end });
372
496
  };
497
+ const todayRows = useMemo(() => {
498
+ const rows = usage?.daily_by_model ?? [];
499
+ if (rows.length === 0) return [];
500
+ const latestDate = rows.map((r) => r.date).sort().slice(-1)[0];
501
+ return rows.filter((r) => r.date === latestDate).map((r) => ({
502
+ model: r.model,
503
+ promptTokens: r.prompt_tokens,
504
+ completionTokens: r.completion_tokens
505
+ })).filter((r) => r.promptTokens + r.completionTokens > 0).sort((a, b) => b.promptTokens + b.completionTokens - (a.promptTokens + a.completionTokens));
506
+ }, [usage]);
507
+ const todayLabel = useMemo(() => {
508
+ const rows = usage?.daily_by_model ?? [];
509
+ if (rows.length === 0) return null;
510
+ return rows.map((r) => r.date).sort().slice(-1)[0];
511
+ }, [usage]);
512
+ const dailyData = useMemo(
513
+ () => (usage?.daily_usage ?? []).map((d) => ({
514
+ date: d.date,
515
+ spend: d.spend,
516
+ promptTokens: d.prompt_tokens,
517
+ completionTokens: d.completion_tokens,
518
+ totalTokens: d.total_tokens,
519
+ apiRequests: d.api_requests,
520
+ successfulRequests: d.successful_requests,
521
+ failedRequests: d.failed_requests
522
+ })),
523
+ [usage]
524
+ );
525
+ const modelRows = useMemo(() => {
526
+ const entries = Object.entries(usage?.usage_by_model ?? {}).map(([model, d]) => ({
527
+ model,
528
+ spend: d.total_spend,
529
+ promptTokens: d.prompt_tokens,
530
+ completionTokens: d.completion_tokens,
531
+ totalTokens: d.total_tokens,
532
+ apiRequests: d.api_requests,
533
+ successfulRequests: d.successful_requests,
534
+ failedRequests: d.failed_requests,
535
+ successRate: d.api_requests > 0 ? d.successful_requests / d.api_requests : 0
536
+ }));
537
+ return selectedModel === "all" ? entries : entries.filter((e) => e.model === selectedModel);
538
+ }, [usage, selectedModel]);
539
+ const keyRows = useMemo(
540
+ () => Object.entries(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({
541
+ keyHash,
542
+ keyAlias: d.key_alias ?? keyHash.slice(0, 8),
543
+ teamId: d.team_id ?? null,
544
+ models: d.models,
545
+ spend: d.total_spend,
546
+ totalTokens: d.total_tokens,
547
+ promptTokens: d.prompt_tokens,
548
+ completionTokens: d.completion_tokens,
549
+ apiRequests: d.api_requests,
550
+ successfulRequests: d.successful_requests,
551
+ failedRequests: d.failed_requests,
552
+ successRate: d.api_requests > 0 ? d.successful_requests / d.api_requests : 0
553
+ })),
554
+ [usage]
555
+ );
556
+ const totalRequests = usage?.api_requests ?? 0;
557
+ const overallSuccessRate = totalRequests > 0 ? (usage?.successful_requests ?? 0) / totalRequests : 0;
373
558
  if (loading) {
374
559
  return /* @__PURE__ */ React3.createElement(Paper3, { sx: { p: 2 } }, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", justifyContent: "center", p: 4 }, /* @__PURE__ */ React3.createElement(CircularProgress2, null)));
375
560
  }
376
- const dailyData = usage?.daily_usage?.map((d) => ({
377
- date: d.date,
378
- spend: d.spend,
379
- promptTokens: d.prompt_tokens,
380
- completionTokens: d.completion_tokens,
381
- totalTokens: d.total_tokens
382
- })) || [];
383
- const usageByModelData = Object.entries(usage?.usage_by_model || {}).map(([model, data]) => ({
384
- model,
385
- spend: data.total_spend,
386
- promptTokens: data.prompt_tokens,
387
- completionTokens: data.completion_tokens
388
- }));
389
- const filteredModelData = selectedModel === "all" ? usageByModelData : usageByModelData.filter((d) => d.model === selectedModel);
390
- return /* @__PURE__ */ React3.createElement(Paper3, { sx: { p: 2 } }, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "h6" }, "Usage Analytics"), /* @__PURE__ */ React3.createElement(Box3, { display: "flex", gap: 2, alignItems: "center" }, /* @__PURE__ */ React3.createElement(FormControl, { size: "small", sx: { minWidth: 140 } }, /* @__PURE__ */ React3.createElement(InputLabel, null, "Period"), /* @__PURE__ */ React3.createElement(
561
+ return /* @__PURE__ */ React3.createElement(Paper3, { sx: { p: 2 } }, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, flexWrap: "wrap", gap: 2 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "h6" }, "Usage Analytics"), /* @__PURE__ */ React3.createElement(Box3, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap" }, /* @__PURE__ */ React3.createElement(FormControl, { size: "small", sx: { minWidth: 140 } }, /* @__PURE__ */ React3.createElement(InputLabel, null, "Period"), /* @__PURE__ */ React3.createElement(
391
562
  Select,
392
563
  {
393
564
  value: selectedPreset,
@@ -397,7 +568,7 @@ var init_UsageStats = __esm({
397
568
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "today" }, "Today"),
398
569
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "7d" }, "Last 7 days"),
399
570
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "30d" }, "Last 30 days")
400
- )), /* @__PURE__ */ React3.createElement(FormControl, { size: "small", sx: { minWidth: 150 } }, /* @__PURE__ */ React3.createElement(InputLabel, null, "Model"), /* @__PURE__ */ React3.createElement(
571
+ )), tab === "models" && /* @__PURE__ */ React3.createElement(FormControl, { size: "small", sx: { minWidth: 180 } }, /* @__PURE__ */ React3.createElement(InputLabel, null, "Model"), /* @__PURE__ */ React3.createElement(
401
572
  Select,
402
573
  {
403
574
  value: selectedModel,
@@ -406,7 +577,28 @@ var init_UsageStats = __esm({
406
577
  },
407
578
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "all" }, "All Models"),
408
579
  models.map((m) => /* @__PURE__ */ React3.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
409
- )))), /* @__PURE__ */ React3.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React3.createElement(Box3, { height: 250 }, /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(AreaChart, { data: dailyData }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { dataKey: "date", tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(YAxis, { tick: { fontSize: 12 }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React3.createElement(Tooltip, { formatter: (value) => [`$${value.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React3.createElement(Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 }))))), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Token Usage by Model"), /* @__PURE__ */ React3.createElement(Box3, { height: 250 }, /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(BarChart, { data: filteredModelData }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { dataKey: "model", tick: { fontSize: 10 } }), /* @__PURE__ */ React3.createElement(YAxis, { tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(Tooltip, null), /* @__PURE__ */ React3.createElement(Legend, null), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "promptTokens", name: "Prompt Tokens", fill: "#8884d8", stackId: "a" }), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "completionTokens", name: "Completion Tokens", fill: "#82ca9d", stackId: "a" }))))), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", gap: 4, flexWrap: "wrap" }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Total Spend"), /* @__PURE__ */ React3.createElement(Typography3, { variant: "h5" }, "$", usage?.total_spend?.toFixed(4) || "0.00")), /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Total Tokens"), /* @__PURE__ */ React3.createElement(Typography3, { variant: "h5" }, usage?.total_tokens?.toLocaleString() || "0")), /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Prompt Tokens"), /* @__PURE__ */ React3.createElement(Typography3, { variant: "h5" }, usage?.prompt_tokens?.toLocaleString() || "0")), /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "text.secondary" }, "Completion Tokens"), /* @__PURE__ */ React3.createElement(Typography3, { variant: "h5" }, usage?.completion_tokens?.toLocaleString() || "0"))))));
580
+ )))), /* @__PURE__ */ React3.createElement(Grid, { container: true, spacing: 2, sx: { mb: 2 } }, /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React3.createElement(KpiCard, { label: "Total Spend", value: fmtUsd(usage?.total_spend ?? 0) })), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React3.createElement(KpiCard, { label: "Total Requests", value: fmtInt(totalRequests), hint: `${fmtInt(usage?.failed_requests ?? 0)} failed` })), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React3.createElement(KpiCard, { label: "Success Rate", value: totalRequests > 0 ? fmtPct(overallSuccessRate) : "\u2014" })), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 6, sm: 3 }, /* @__PURE__ */ React3.createElement(
581
+ KpiCard,
582
+ {
583
+ label: "Total Tokens",
584
+ value: fmtInt(usage?.total_tokens ?? 0),
585
+ hint: `${fmtInt(usage?.prompt_tokens ?? 0)} in \xB7 ${fmtInt(usage?.completion_tokens ?? 0)} out`
586
+ }
587
+ ))), /* @__PURE__ */ React3.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 2 } }, /* @__PURE__ */ React3.createElement(Tab, { value: "costs", label: "Costs" }), /* @__PURE__ */ React3.createElement(Tab, { value: "models", label: "Model Activity" }), /* @__PURE__ */ React3.createElement(Tab, { value: "keys", label: "Key Activity" })), tab === "costs" && /* @__PURE__ */ React3.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React3.createElement(Box3, { height: 260 }, /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(AreaChart, { data: dailyData }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { dataKey: "date", tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(YAxis, { tick: { fontSize: 12 }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React3.createElement(Tooltip, { formatter: (value) => [`$${value.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React3.createElement(Area, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 }))))), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Requests"), /* @__PURE__ */ React3.createElement(Box3, { height: 260 }, /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(BarChart, { data: dailyData }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { dataKey: "date", tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(YAxis, { tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(Tooltip, null), /* @__PURE__ */ React3.createElement(Legend, null), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: "#82ca9d", stackId: "r" }), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: "#e57373", stackId: "r" }))))), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, todayLabel ? `Tokens In / Out by Model \u2014 ${todayLabel}` : "Tokens In / Out by Model \u2014 Today"), /* @__PURE__ */ React3.createElement(Box3, { height: 300 }, todayRows.length === 0 ? /* @__PURE__ */ React3.createElement(Box3, { display: "flex", alignItems: "center", justifyContent: "center", height: "100%" }, /* @__PURE__ */ React3.createElement(Typography3, { color: "text.secondary" }, "No token activity for the latest day")) : /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(BarChart, { data: todayRows, layout: "vertical", margin: { left: 60 } }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { type: "number", tick: { fontSize: 12 }, tickFormatter: fmtInt }), /* @__PURE__ */ React3.createElement(YAxis, { type: "category", dataKey: "model", tick: { fontSize: 11 }, width: 180 }), /* @__PURE__ */ React3.createElement(Tooltip, { formatter: (value) => fmtInt(value) }), /* @__PURE__ */ React3.createElement(Legend, null), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "promptTokens", name: "Input (prompt)", fill: "#8884d8", stackId: "io" }), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "completionTokens", name: "Output (completion)", fill: "#82ca9d", stackId: "io" })))))), tab === "models" && /* @__PURE__ */ React3.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React3.createElement(Typography3, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Tokens by Model"), /* @__PURE__ */ React3.createElement(Box3, { height: 260 }, /* @__PURE__ */ React3.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React3.createElement(BarChart, { data: modelRows }, /* @__PURE__ */ React3.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React3.createElement(XAxis, { dataKey: "model", tick: { fontSize: 10 }, interval: 0, angle: -15, textAnchor: "end", height: 60 }), /* @__PURE__ */ React3.createElement(YAxis, { tick: { fontSize: 12 } }), /* @__PURE__ */ React3.createElement(Tooltip, null), /* @__PURE__ */ React3.createElement(Legend, null), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "promptTokens", name: "Prompt", fill: "#8884d8", stackId: "t" }), /* @__PURE__ */ React3.createElement(Bar, { dataKey: "completionTokens", name: "Completion", fill: "#82ca9d", stackId: "t" }))))), /* @__PURE__ */ React3.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React3.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React3.createElement(Table2, { size: "small" }, /* @__PURE__ */ React3.createElement(TableHead2, null, /* @__PURE__ */ React3.createElement(TableRow2, null, /* @__PURE__ */ React3.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Prompt"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Completion"), /* @__PURE__ */ React3.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React3.createElement(TableBody2, null, modelRows.length === 0 ? /* @__PURE__ */ React3.createElement(TableRow2, null, /* @__PURE__ */ React3.createElement(TableCell2, { colSpan: 8, align: "center" }, "No model activity")) : modelRows.sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React3.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React3.createElement(TableCell2, null, r.model), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.promptTokens)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.completionTokens)), /* @__PURE__ */ React3.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React3.createElement(Box3, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React3.createElement(
588
+ LinearProgress2,
589
+ {
590
+ variant: "determinate",
591
+ value: r.successRate * 100,
592
+ sx: { flex: 1, height: 6, borderRadius: 3 }
593
+ }
594
+ ), /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))))), tab === "keys" && /* @__PURE__ */ React3.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React3.createElement(Table2, { size: "small" }, /* @__PURE__ */ React3.createElement(TableHead2, null, /* @__PURE__ */ React3.createElement(TableRow2, null, /* @__PURE__ */ React3.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React3.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React3.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React3.createElement(TableBody2, null, keyRows.length === 0 ? /* @__PURE__ */ React3.createElement(TableRow2, null, /* @__PURE__ */ React3.createElement(TableCell2, { colSpan: 7, align: "center" }, "No key activity")) : keyRows.sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React3.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React3.createElement(TableCell2, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2" }, r.keyAlias), r.teamId ? /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React3.createElement(TableCell2, null, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.map((m) => /* @__PURE__ */ React3.createElement(Chip3, { key: m, label: m, size: "small", variant: "outlined" })))), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.totalTokens)), /* @__PURE__ */ React3.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React3.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React3.createElement(Box3, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React3.createElement(
595
+ LinearProgress2,
596
+ {
597
+ variant: "determinate",
598
+ value: r.successRate * 100,
599
+ sx: { flex: 1, height: 6, borderRadius: 3 }
600
+ }
601
+ ), /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))));
410
602
  };
411
603
  }
412
604
  });
@@ -417,14 +609,14 @@ import {
417
609
  Paper as Paper4,
418
610
  Box as Box4,
419
611
  Typography as Typography4,
420
- LinearProgress as LinearProgress2,
421
- Chip as Chip3,
422
- Table as Table2,
423
- TableBody as TableBody2,
424
- TableCell as TableCell2,
425
- TableHead as TableHead2,
426
- TableRow as TableRow2,
427
- TableContainer as TableContainer2,
612
+ LinearProgress as LinearProgress3,
613
+ Chip as Chip4,
614
+ Table as Table3,
615
+ TableBody as TableBody3,
616
+ TableCell as TableCell3,
617
+ TableHead as TableHead3,
618
+ TableRow as TableRow3,
619
+ TableContainer as TableContainer3,
428
620
  Collapse,
429
621
  IconButton as IconButton2,
430
622
  CircularProgress as CircularProgress3
@@ -451,16 +643,16 @@ var init_TeamUsage = __esm({
451
643
  const isOver = budget > 0 && spend >= budget;
452
644
  const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;
453
645
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
454
- return /* @__PURE__ */ React4.createElement(Paper4, { variant: "outlined", sx: { p: 2, mb: 2 } }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(Group, { color: "action" }), /* @__PURE__ */ React4.createElement(Box4, { flexGrow: 1 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle1", fontWeight: 600 }, team.team_alias ?? team.team_id), team.models?.length ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, team.models.map((m) => /* @__PURE__ */ React4.createElement(Chip3, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary" }, "All models")), /* @__PURE__ */ React4.createElement(IconButton2, { size: "small", onClick: () => setExpanded((e) => !e) }, expanded ? /* @__PURE__ */ React4.createElement(ExpandLess, null) : /* @__PURE__ */ React4.createElement(ExpandMore, null))), budget > 0 && /* @__PURE__ */ React4.createElement(Box4, { mt: 1.5 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "space-between", mb: 0.5 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2" }, "$", spend.toFixed(2), " / $", budget.toFixed(2)), isOver && /* @__PURE__ */ React4.createElement(Chip3, { label: "Over Budget", size: "small", color: "error" }), isNear && /* @__PURE__ */ React4.createElement(Chip3, { label: "Near Limit", size: "small", color: "warning" })), /* @__PURE__ */ React4.createElement(
455
- LinearProgress2,
646
+ return /* @__PURE__ */ React4.createElement(Paper4, { variant: "outlined", sx: { p: 2, mb: 2 } }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(Group, { color: "action" }), /* @__PURE__ */ React4.createElement(Box4, { flexGrow: 1 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle1", fontWeight: 600 }, team.team_alias ?? team.team_id), team.models?.length ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap", mt: 0.5 }, team.models.map((m) => /* @__PURE__ */ React4.createElement(Chip4, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary" }, "All models")), /* @__PURE__ */ React4.createElement(IconButton2, { size: "small", onClick: () => setExpanded((e) => !e) }, expanded ? /* @__PURE__ */ React4.createElement(ExpandLess, null) : /* @__PURE__ */ React4.createElement(ExpandMore, null))), budget > 0 && /* @__PURE__ */ React4.createElement(Box4, { mt: 1.5 }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "space-between", mb: 0.5 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2" }, "$", spend.toFixed(2), " / $", budget.toFixed(2)), isOver && /* @__PURE__ */ React4.createElement(Chip4, { label: "Over Budget", size: "small", color: "error" }), isNear && /* @__PURE__ */ React4.createElement(Chip4, { label: "Near Limit", size: "small", color: "warning" })), /* @__PURE__ */ React4.createElement(
647
+ LinearProgress3,
456
648
  {
457
649
  variant: "determinate",
458
650
  value: budgetPct,
459
651
  color: isOver ? "error" : isNear ? "warning" : "primary",
460
652
  sx: { height: 6, borderRadius: 1 }
461
653
  }
462
- )), /* @__PURE__ */ React4.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, usageLoading ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React4.createElement(CircularProgress3, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React4.createElement(Box4, { height: 160 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid2, { strokeDasharray: "3 3" }), /* @__PURE__ */ React4.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11 } }), /* @__PURE__ */ React4.createElement(YAxis2, { tick: { fontSize: 11 }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React4.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 }))))) : null, team.members_with_roles?.length ? /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Members"), /* @__PURE__ */ React4.createElement(TableContainer2, null, /* @__PURE__ */ React4.createElement(Table2, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead2, null, /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, null, "User"), /* @__PURE__ */ React4.createElement(TableCell2, null, "Role"))), /* @__PURE__ */ React4.createElement(TableBody2, null, team.members_with_roles.map((m) => /* @__PURE__ */ React4.createElement(TableRow2, { key: m.user_id }, /* @__PURE__ */ React4.createElement(TableCell2, { sx: { fontFamily: "monospace", fontSize: 12 } }, m.user_id), /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(
463
- Chip3,
654
+ )), /* @__PURE__ */ React4.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, usageLoading ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React4.createElement(CircularProgress3, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React4.createElement(Box4, { height: 160 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid2, { strokeDasharray: "3 3" }), /* @__PURE__ */ React4.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11 } }), /* @__PURE__ */ React4.createElement(YAxis2, { tick: { fontSize: 11 }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React4.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 }))))) : null, team.members_with_roles?.length ? /* @__PURE__ */ React4.createElement(Box4, { mt: 2 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Members"), /* @__PURE__ */ React4.createElement(TableContainer3, null, /* @__PURE__ */ React4.createElement(Table3, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead3, null, /* @__PURE__ */ React4.createElement(TableRow3, null, /* @__PURE__ */ React4.createElement(TableCell3, null, "User"), /* @__PURE__ */ React4.createElement(TableCell3, null, "Role"))), /* @__PURE__ */ React4.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React4.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React4.createElement(TableCell3, { sx: { fontFamily: "monospace", fontSize: 12 } }, m.user_id), /* @__PURE__ */ React4.createElement(TableCell3, null, /* @__PURE__ */ React4.createElement(
655
+ Chip4,
464
656
  {
465
657
  label: m.role,
466
658
  size: "small",
@@ -475,7 +667,7 @@ var init_TeamUsage = __esm({
475
667
  getTeamUsageLoading
476
668
  }) => {
477
669
  if (loading) {
478
- return /* @__PURE__ */ React4.createElement(Paper4, { sx: { p: 2 } }, /* @__PURE__ */ React4.createElement(LinearProgress2, null));
670
+ return /* @__PURE__ */ React4.createElement(Paper4, { sx: { p: 2 } }, /* @__PURE__ */ React4.createElement(LinearProgress3, null));
479
671
  }
480
672
  if (!teams.length) {
481
673
  return /* @__PURE__ */ React4.createElement(Paper4, { sx: { p: 2 } }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(Group, { color: "disabled" }), /* @__PURE__ */ React4.createElement(Typography4, { color: "text.secondary", variant: "body2" }, "No team membership found in LiteLLM for this account.")));
@@ -498,7 +690,7 @@ var LiteLLMPage_exports = {};
498
690
  __export(LiteLLMPage_exports, {
499
691
  LiteLLMPage: () => LiteLLMPage
500
692
  });
501
- import React5, { useState as useState4, useCallback, useMemo } from "react";
693
+ import React5, { useState as useState4, useCallback, useMemo as useMemo2 } from "react";
502
694
  import { Grid as Grid2, Box as Box5, Snackbar, Alert, CircularProgress as CircularProgress4, Typography as Typography5, Paper as Paper5 } from "@mui/material";
503
695
  import { useAsync, useAsyncRetry } from "react-use";
504
696
  import { useApi } from "@backstage/core-plugin-api";
@@ -545,7 +737,7 @@ var init_LiteLLMPage = __esm({
545
737
  () => api.getTeams().catch(() => []),
546
738
  [api]
547
739
  );
548
- const teams = useMemo(() => {
740
+ const teams = useMemo2(() => {
549
741
  if (!allTeams?.length) return [];
550
742
  if (!userInfo) return allTeams;
551
743
  const userId = userInfo.user_id;
@@ -576,7 +768,7 @@ var init_LiteLLMPage = __esm({
576
768
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
577
769
  }
578
770
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
579
- const allowedModels = useMemo(() => {
771
+ const allowedModels = useMemo2(() => {
580
772
  if (!allModels?.length) return [];
581
773
  const userModels = userInfo?.models;
582
774
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -598,6 +790,19 @@ var init_LiteLLMPage = __esm({
598
790
  },
599
791
  [api, refreshKeys]
600
792
  );
793
+ const handleUpdateKey = useCallback(
794
+ async (keyId, request) => {
795
+ try {
796
+ await api.updateKey(keyId, request);
797
+ setSnackbar({ message: "Key updated successfully", severity: "success" });
798
+ refreshKeys();
799
+ } catch (e) {
800
+ setSnackbar({ message: `Failed to update key: ${e.message}`, severity: "error" });
801
+ throw e;
802
+ }
803
+ },
804
+ [api, refreshKeys]
805
+ );
601
806
  const handleDeleteKey = useCallback(
602
807
  async (keyId) => {
603
808
  try {
@@ -639,6 +844,7 @@ var init_LiteLLMPage = __esm({
639
844
  teams: teams ?? [],
640
845
  loading: keysLoading || modelsLoading,
641
846
  onGenerateKey: handleGenerateKey,
847
+ onUpdateKey: handleUpdateKey,
642
848
  onDeleteKey: handleDeleteKey
643
849
  }
644
850
  )), /* @__PURE__ */ React5.createElement(Grid2, { item: true, xs: 12 }, /* @__PURE__ */ React5.createElement(
@@ -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": "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",
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"]
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, { useEffect, 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 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 // Default to the user's first team when the modal opens so users in exactly\n // one team don't need to pick. Multi-team users see the picker pre-filled\n // and can change it. Teams are already filtered to the current user upstream.\n useEffect(() => {\n if (!generateModalOpen) return;\n if (!formData.team_id && teams.length > 0) {\n setFormData(f => ({ ...f, team_id: teams[0].team_id }));\n }\n }, [generateModalOpen, teams, formData.team_id]);\n\n const teamRequired = teams.length > 0;\n const canGenerate = !teamRequired || !!formData.team_id;\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=\"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\n {...params}\n label=\"Team\"\n required\n error={teamRequired && !formData.team_id}\n helperText={\n teamRequired && !formData.team_id\n ? 'Pick a team to scope this key'\n : undefined\n }\n fullWidth\n />\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\n onClick={handleGenerate}\n variant=\"contained\"\n color=\"primary\"\n disabled={submitting || !canGenerate}\n >\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, { useMemo, useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n CircularProgress,\n Tabs,\n Tab,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Chip,\n LinearProgress,\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 {\n DateRange,\n UsageMetrics,\n ModelInfo,\n UsageModelBreakdown,\n UsageKeyBreakdown,\n} 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';\ntype TabKey = 'costs' | 'models' | 'keys';\n\nconst fmtUsd = (n: number) => `$${(n ?? 0).toFixed(n < 1 ? 4 : 2)}`;\nconst fmtInt = (n: number) => (n ?? 0).toLocaleString();\nconst fmtPct = (n: number) => `${(n * 100).toFixed(1)}%`;\n\nconst KpiCard: React.FC<{ label: string; value: string; hint?: string }> = ({ label, value, hint }) => (\n <Paper variant=\"outlined\" sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"caption\" color=\"text.secondary\">{label}</Typography>\n <Typography variant=\"h5\" sx={{ mt: 0.5 }}>{value}</Typography>\n {hint ? <Typography variant=\"caption\" color=\"text.secondary\">{hint}</Typography> : null}\n </Paper>\n);\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 const [tab, setTab] = useState<TabKey>('costs');\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 const todayRows = useMemo(() => {\n const rows = usage?.daily_by_model ?? [];\n if (rows.length === 0) return [];\n // Pick the most recent date present in the data \u2014 that's the latest\n // \"current day\" the proxy has aggregated, even if today has no traffic yet.\n const latestDate = rows.map(r => r.date).sort().slice(-1)[0];\n return rows\n .filter(r => r.date === latestDate)\n .map(r => ({\n model: r.model,\n promptTokens: r.prompt_tokens,\n completionTokens: r.completion_tokens,\n }))\n .filter(r => r.promptTokens + r.completionTokens > 0)\n .sort((a, b) => (b.promptTokens + b.completionTokens) - (a.promptTokens + a.completionTokens));\n }, [usage]);\n\n const todayLabel = useMemo(() => {\n const rows = usage?.daily_by_model ?? [];\n if (rows.length === 0) return null;\n return rows.map(r => r.date).sort().slice(-1)[0];\n }, [usage]);\n\n const dailyData = useMemo(\n () =>\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 apiRequests: d.api_requests,\n successfulRequests: d.successful_requests,\n failedRequests: d.failed_requests,\n })),\n [usage],\n );\n\n const modelRows = useMemo(() => {\n const entries = Object.entries<UsageModelBreakdown>(usage?.usage_by_model ?? {}).map(([model, d]) => ({\n model,\n spend: d.total_spend,\n promptTokens: d.prompt_tokens,\n completionTokens: d.completion_tokens,\n totalTokens: d.total_tokens,\n apiRequests: d.api_requests,\n successfulRequests: d.successful_requests,\n failedRequests: d.failed_requests,\n successRate: d.api_requests > 0 ? d.successful_requests / d.api_requests : 0,\n }));\n return selectedModel === 'all' ? entries : entries.filter(e => e.model === selectedModel);\n }, [usage, selectedModel]);\n\n const keyRows = useMemo(\n () =>\n Object.entries<UsageKeyBreakdown>(usage?.usage_by_key ?? {}).map(([keyHash, d]) => ({\n keyHash,\n keyAlias: d.key_alias ?? keyHash.slice(0, 8),\n teamId: d.team_id ?? null,\n models: d.models,\n spend: d.total_spend,\n totalTokens: d.total_tokens,\n promptTokens: d.prompt_tokens,\n completionTokens: d.completion_tokens,\n apiRequests: d.api_requests,\n successfulRequests: d.successful_requests,\n failedRequests: d.failed_requests,\n successRate: d.api_requests > 0 ? d.successful_requests / d.api_requests : 0,\n })),\n [usage],\n );\n\n const totalRequests = usage?.api_requests ?? 0;\n const overallSuccessRate = totalRequests > 0 ? (usage?.successful_requests ?? 0) / totalRequests : 0;\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 return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={2} flexWrap=\"wrap\" gap={2}>\n <Typography variant=\"h6\">Usage Analytics</Typography>\n <Box display=\"flex\" gap={2} alignItems=\"center\" flexWrap=\"wrap\">\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 {tab === 'models' && (\n <FormControl size=\"small\" sx={{ minWidth: 180 }}>\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 )}\n </Box>\n </Box>\n\n <Grid container spacing={2} sx={{ mb: 2 }}>\n <Grid item xs={6} sm={3}>\n <KpiCard label=\"Total Spend\" value={fmtUsd(usage?.total_spend ?? 0)} />\n </Grid>\n <Grid item xs={6} sm={3}>\n <KpiCard label=\"Total Requests\" value={fmtInt(totalRequests)} hint={`${fmtInt(usage?.failed_requests ?? 0)} failed`} />\n </Grid>\n <Grid item xs={6} sm={3}>\n <KpiCard label=\"Success Rate\" value={totalRequests > 0 ? fmtPct(overallSuccessRate) : '\u2014'} />\n </Grid>\n <Grid item xs={6} sm={3}>\n <KpiCard\n label=\"Total Tokens\"\n value={fmtInt(usage?.total_tokens ?? 0)}\n hint={`${fmtInt(usage?.prompt_tokens ?? 0)} in \u00B7 ${fmtInt(usage?.completion_tokens ?? 0)} out`}\n />\n </Grid>\n </Grid>\n\n <Tabs value={tab} onChange={(_, v) => setTab(v as TabKey)} sx={{ mb: 2 }}>\n <Tab value=\"costs\" label=\"Costs\" />\n <Tab value=\"models\" label=\"Model Activity\" />\n <Tab value=\"keys\" label=\"Key Activity\" />\n </Tabs>\n\n {tab === 'costs' && (\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={260}>\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 <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Requests\n </Typography>\n <Box height={260}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 12 }} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"successfulRequests\" name=\"Successful\" fill=\"#82ca9d\" stackId=\"r\" />\n <Bar dataKey=\"failedRequests\" name=\"Failed\" fill=\"#e57373\" stackId=\"r\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n <Grid item xs={12}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n {todayLabel ? `Tokens In / Out by Model \u2014 ${todayLabel}` : 'Tokens In / Out by Model \u2014 Today'}\n </Typography>\n <Box height={300}>\n {todayRows.length === 0 ? (\n <Box display=\"flex\" alignItems=\"center\" justifyContent=\"center\" height=\"100%\">\n <Typography color=\"text.secondary\">No token activity for the latest day</Typography>\n </Box>\n ) : (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={todayRows} layout=\"vertical\" margin={{ left: 60 }}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis type=\"number\" tick={{ fontSize: 12 }} tickFormatter={fmtInt} />\n <YAxis type=\"category\" dataKey=\"model\" tick={{ fontSize: 11 }} width={180} />\n <Tooltip formatter={(value: number) => fmtInt(value)} />\n <Legend />\n <Bar dataKey=\"promptTokens\" name=\"Input (prompt)\" fill=\"#8884d8\" stackId=\"io\" />\n <Bar dataKey=\"completionTokens\" name=\"Output (completion)\" fill=\"#82ca9d\" stackId=\"io\" />\n </BarChart>\n </ResponsiveContainer>\n )}\n </Box>\n </Grid>\n </Grid>\n )}\n\n {tab === 'models' && (\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Tokens by Model\n </Typography>\n <Box height={260}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={modelRows}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"model\" tick={{ fontSize: 10 }} interval={0} angle={-15} textAnchor=\"end\" height={60} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"promptTokens\" name=\"Prompt\" fill=\"#8884d8\" stackId=\"t\" />\n <Bar dataKey=\"completionTokens\" name=\"Completion\" fill=\"#82ca9d\" stackId=\"t\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n <Grid item xs={12}>\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Model</TableCell>\n <TableCell align=\"right\">Spend</TableCell>\n <TableCell align=\"right\">Requests</TableCell>\n <TableCell align=\"right\">Success</TableCell>\n <TableCell align=\"right\">Failed</TableCell>\n <TableCell align=\"right\">Prompt</TableCell>\n <TableCell align=\"right\">Completion</TableCell>\n <TableCell sx={{ minWidth: 120 }}>Success rate</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {modelRows.length === 0 ? (\n <TableRow><TableCell colSpan={8} align=\"center\">No model activity</TableCell></TableRow>\n ) : modelRows\n .sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens)\n .map(r => (\n <TableRow key={r.model}>\n <TableCell>{r.model}</TableCell>\n <TableCell align=\"right\">{fmtUsd(r.spend)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.apiRequests)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.successfulRequests)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.failedRequests)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.promptTokens)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.completionTokens)}</TableCell>\n <TableCell>\n {r.apiRequests > 0 ? (\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <LinearProgress\n variant=\"determinate\"\n value={r.successRate * 100}\n sx={{ flex: 1, height: 6, borderRadius: 3 }}\n />\n <Typography variant=\"caption\">{fmtPct(r.successRate)}</Typography>\n </Box>\n ) : '\u2014'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Grid>\n </Grid>\n )}\n\n {tab === 'keys' && (\n <TableContainer component={Paper} variant=\"outlined\">\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>Key</TableCell>\n <TableCell>Models</TableCell>\n <TableCell align=\"right\">Spend</TableCell>\n <TableCell align=\"right\">Requests</TableCell>\n <TableCell align=\"right\">Tokens</TableCell>\n <TableCell align=\"right\">Failed</TableCell>\n <TableCell sx={{ minWidth: 120 }}>Success rate</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {keyRows.length === 0 ? (\n <TableRow><TableCell colSpan={7} align=\"center\">No key activity</TableCell></TableRow>\n ) : keyRows\n .sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests)\n .map(r => (\n <TableRow key={r.keyHash}>\n <TableCell>\n <Typography variant=\"body2\">{r.keyAlias}</Typography>\n {r.teamId ? (\n <Typography variant=\"caption\" color=\"text.secondary\">team: {r.teamId}</Typography>\n ) : null}\n </TableCell>\n <TableCell>\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\">\n {r.models.map(m => (\n <Chip key={m} label={m} size=\"small\" variant=\"outlined\" />\n ))}\n </Box>\n </TableCell>\n <TableCell align=\"right\">{fmtUsd(r.spend)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.apiRequests)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.totalTokens)}</TableCell>\n <TableCell align=\"right\">{fmtInt(r.failedRequests)}</TableCell>\n <TableCell>\n {r.apiRequests > 0 ? (\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <LinearProgress\n variant=\"determinate\"\n value={r.successRate * 100}\n sx={{ flex: 1, height: 6, borderRadius: 3 }}\n />\n <Typography variant=\"caption\">{fmtPct(r.successRate)}</Typography>\n </Box>\n ) : '\u2014'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n )}\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
+ "names": ["React", "Paper", "Box", "Typography", "Chip", "React", "useState", "Paper", "Box", "Typography", "MenuItem", "CircularProgress", "Table", "TableBody", "TableCell", "TableContainer", "TableHead", "TableRow", "Chip", "LinearProgress", "React", "useState", "Paper", "Box", "Typography", "LinearProgress", "Chip", "Table", "TableBody", "TableCell", "TableHead", "TableRow", "TableContainer", "IconButton", "CircularProgress", "AreaChart", "Area", "XAxis", "YAxis", "CartesianGrid", "Tooltip", "ResponsiveContainer", "React", "useState", "useMemo", "Grid", "Box", "CircularProgress", "Typography", "Paper", "React", "LiteLLMPage"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acarmisc/backstage-plugin-litellm",
3
- "version": "0.1.8",
3
+ "version": "0.1.11",
4
4
  "backstage": {
5
5
  "role": "frontend-plugin",
6
6
  "pluginId": "litellm",