@acarmisc/backstage-plugin-litellm 0.1.10 → 0.1.12

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
@@ -143,7 +143,7 @@ var init_DashboardHeader = __esm({
143
143
  });
144
144
 
145
145
  // src/components/KeysTable.tsx
146
- import React2, { useState } from "react";
146
+ import React2, { useEffect, useState } from "react";
147
147
  import {
148
148
  Paper as Paper2,
149
149
  Table,
@@ -167,15 +167,10 @@ import {
167
167
  Autocomplete
168
168
  } from "@mui/material";
169
169
  import { ContentCopy, Delete, Add, Edit, Visibility, VisibilityOff } from "@mui/icons-material";
170
- var KEY_TYPES, maskKey, formatDate, emptyForm, keyToEditForm, KeysTable;
170
+ var maskKey, formatDate, emptyForm, keyToEditForm, KeysTable;
171
171
  var init_KeysTable = __esm({
172
172
  "src/components/KeysTable.tsx"() {
173
173
  "use strict";
174
- KEY_TYPES = [
175
- { value: "llm_api", label: "AI API" },
176
- { value: "management", label: "Management" },
177
- { value: "read_only", label: "Read Only" }
178
- ];
179
174
  maskKey = (key) => {
180
175
  if (key.length <= 8) return "***";
181
176
  return `${key.slice(0, 4)}...${key.slice(-4)}`;
@@ -217,6 +212,14 @@ var init_KeysTable = __esm({
217
212
  const [newKeyValue, setNewKeyValue] = useState(null);
218
213
  const [formData, setFormData] = useState(emptyForm());
219
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;
220
223
  const [editingKey, setEditingKey] = useState(null);
221
224
  const [editForm, setEditForm] = useState({});
222
225
  const [editSubmitting, setEditSubmitting] = useState(false);
@@ -306,16 +309,6 @@ var init_KeysTable = __esm({
306
309
  onChange: (e) => setFormData({ ...formData, alias: e.target.value }),
307
310
  fullWidth: true
308
311
  }
309
- ), /* @__PURE__ */ React2.createElement(
310
- TextField,
311
- {
312
- select: true,
313
- label: "Key Type",
314
- value: formData.key_type || "llm_api",
315
- onChange: (e) => setFormData({ ...formData, key_type: e.target.value }),
316
- fullWidth: true
317
- },
318
- KEY_TYPES.map((kt) => /* @__PURE__ */ React2.createElement(MenuItem, { key: kt.value, value: kt.value }, kt.label))
319
312
  ), /* @__PURE__ */ React2.createElement(
320
313
  TextField,
321
314
  {
@@ -337,7 +330,17 @@ var init_KeysTable = __esm({
337
330
  getOptionLabel: (t) => t.team_alias || t.team_id,
338
331
  value: selectedTeam,
339
332
  onChange: (_e, team) => setFormData({ ...formData, team_id: team?.team_id }),
340
- 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
+ )
341
344
  }
342
345
  ), models.length > 0 && /* @__PURE__ */ React2.createElement(
343
346
  Autocomplete,
@@ -369,7 +372,16 @@ var init_KeysTable = __esm({
369
372
  onChange: (e) => setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : void 0 }),
370
373
  fullWidth: true
371
374
  }
372
- ))), /* @__PURE__ */ React2.createElement(DialogActions, null, /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal }, newKeyValue ? "Done" : "Cancel"), !newKeyValue && /* @__PURE__ */ React2.createElement(Button, { onClick: handleGenerate, variant: "contained", color: "primary", disabled: submitting }, submitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Generate"))), /* @__PURE__ */ React2.createElement(Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, "Edit Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, editingKey && /* @__PURE__ */ React2.createElement(Box2, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React2.createElement("code", { style: { fontFamily: "monospace" } }, maskKey(editingKey.key))), /* @__PURE__ */ React2.createElement(
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(
373
385
  TextField,
374
386
  {
375
387
  label: "Alias",
@@ -421,7 +433,7 @@ var init_KeysTable = __esm({
421
433
  });
422
434
 
423
435
  // src/components/UsageStats.tsx
424
- import React3, { useState as useState2 } from "react";
436
+ import React3, { useMemo, useState as useState2 } from "react";
425
437
  import {
426
438
  Paper as Paper3,
427
439
  Box as Box3,
@@ -431,7 +443,17 @@ import {
431
443
  Select,
432
444
  MenuItem as MenuItem2,
433
445
  Grid,
434
- 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
435
457
  } from "@mui/material";
436
458
  import {
437
459
  AreaChart,
@@ -445,10 +467,14 @@ import {
445
467
  ResponsiveContainer,
446
468
  Legend
447
469
  } from "recharts";
448
- var UsageStats;
470
+ var fmtUsd, fmtInt, fmtPct, KpiCard, UsageStats;
449
471
  var init_UsageStats = __esm({
450
472
  "src/components/UsageStats.tsx"() {
451
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);
452
478
  UsageStats = ({
453
479
  usage,
454
480
  models,
@@ -458,6 +484,7 @@ var init_UsageStats = __esm({
458
484
  }) => {
459
485
  const [selectedPreset, setSelectedPreset] = useState2("7d");
460
486
  const [selectedModel, setSelectedModel] = useState2("all");
487
+ const [tab, setTab] = useState2("costs");
461
488
  const handlePresetChange = (preset) => {
462
489
  setSelectedPreset(preset);
463
490
  const end = /* @__PURE__ */ new Date();
@@ -467,24 +494,71 @@ var init_UsageStats = __esm({
467
494
  else if (preset === "30d") start.setDate(start.getDate() - 30);
468
495
  onDateRangeChange({ start, end });
469
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;
470
558
  if (loading) {
471
559
  return /* @__PURE__ */ React3.createElement(Paper3, { sx: { p: 2 } }, /* @__PURE__ */ React3.createElement(Box3, { display: "flex", justifyContent: "center", p: 4 }, /* @__PURE__ */ React3.createElement(CircularProgress2, null)));
472
560
  }
473
- const dailyData = usage?.daily_usage?.map((d) => ({
474
- date: d.date,
475
- spend: d.spend,
476
- promptTokens: d.prompt_tokens,
477
- completionTokens: d.completion_tokens,
478
- totalTokens: d.total_tokens
479
- })) || [];
480
- const usageByModelData = Object.entries(usage?.usage_by_model || {}).map(([model, data]) => ({
481
- model,
482
- spend: data.total_spend,
483
- promptTokens: data.prompt_tokens,
484
- completionTokens: data.completion_tokens
485
- }));
486
- const filteredModelData = selectedModel === "all" ? usageByModelData : usageByModelData.filter((d) => d.model === selectedModel);
487
- 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(
488
562
  Select,
489
563
  {
490
564
  value: selectedPreset,
@@ -494,7 +568,7 @@ var init_UsageStats = __esm({
494
568
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "today" }, "Today"),
495
569
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "7d" }, "Last 7 days"),
496
570
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "30d" }, "Last 30 days")
497
- )), /* @__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(
498
572
  Select,
499
573
  {
500
574
  value: selectedModel,
@@ -503,7 +577,28 @@ var init_UsageStats = __esm({
503
577
  },
504
578
  /* @__PURE__ */ React3.createElement(MenuItem2, { value: "all" }, "All Models"),
505
579
  models.map((m) => /* @__PURE__ */ React3.createElement(MenuItem2, { key: m.model_name, value: m.model_name }, m.model_name))
506
- )))), /* @__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")))))));
507
602
  };
508
603
  }
509
604
  });
@@ -514,14 +609,14 @@ import {
514
609
  Paper as Paper4,
515
610
  Box as Box4,
516
611
  Typography as Typography4,
517
- LinearProgress as LinearProgress2,
518
- Chip as Chip3,
519
- Table as Table2,
520
- TableBody as TableBody2,
521
- TableCell as TableCell2,
522
- TableHead as TableHead2,
523
- TableRow as TableRow2,
524
- 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,
525
620
  Collapse,
526
621
  IconButton as IconButton2,
527
622
  CircularProgress as CircularProgress3
@@ -548,16 +643,16 @@ var init_TeamUsage = __esm({
548
643
  const isOver = budget > 0 && spend >= budget;
549
644
  const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;
550
645
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
551
- 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(
552
- 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,
553
648
  {
554
649
  variant: "determinate",
555
650
  value: budgetPct,
556
651
  color: isOver ? "error" : isNear ? "warning" : "primary",
557
652
  sx: { height: 6, borderRadius: 1 }
558
653
  }
559
- )), /* @__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(
560
- 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,
561
656
  {
562
657
  label: m.role,
563
658
  size: "small",
@@ -572,7 +667,7 @@ var init_TeamUsage = __esm({
572
667
  getTeamUsageLoading
573
668
  }) => {
574
669
  if (loading) {
575
- 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));
576
671
  }
577
672
  if (!teams.length) {
578
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.")));
@@ -595,7 +690,7 @@ var LiteLLMPage_exports = {};
595
690
  __export(LiteLLMPage_exports, {
596
691
  LiteLLMPage: () => LiteLLMPage
597
692
  });
598
- import React5, { useState as useState4, useCallback, useMemo } from "react";
693
+ import React5, { useState as useState4, useCallback, useMemo as useMemo2 } from "react";
599
694
  import { Grid as Grid2, Box as Box5, Snackbar, Alert, CircularProgress as CircularProgress4, Typography as Typography5, Paper as Paper5 } from "@mui/material";
600
695
  import { useAsync, useAsyncRetry } from "react-use";
601
696
  import { useApi } from "@backstage/core-plugin-api";
@@ -642,7 +737,7 @@ var init_LiteLLMPage = __esm({
642
737
  () => api.getTeams().catch(() => []),
643
738
  [api]
644
739
  );
645
- const teams = useMemo(() => {
740
+ const teams = useMemo2(() => {
646
741
  if (!allTeams?.length) return [];
647
742
  if (!userInfo) return allTeams;
648
743
  const userId = userInfo.user_id;
@@ -673,7 +768,7 @@ var init_LiteLLMPage = __esm({
673
768
  setTeamUsageLoading((prev) => ({ ...prev, [teamId]: false }));
674
769
  }
675
770
  }, [api, dateRange, teamUsageCache, teamUsageLoading]);
676
- const allowedModels = useMemo(() => {
771
+ const allowedModels = useMemo2(() => {
677
772
  if (!allModels?.length) return [];
678
773
  const userModels = userInfo?.models;
679
774
  const teamModels = teams?.flatMap((t) => t.models ?? []);
@@ -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 UpdateKeyRequest,\n} from './types';\n\nclass ApiError extends Error {\n body: unknown;\n status: number;\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nexport interface LiteLlmApiInterface {\n getUserInfo(): Promise<UserInfo>;\n listKeys(): Promise<VirtualKey[]>;\n generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse>;\n updateKey(keyId: string, request: UpdateKeyRequest): Promise<VirtualKey>;\n deleteKey(keyId: string): Promise<{ success: boolean }>;\n listModels(): Promise<ModelInfo[]>;\n getTeams(): Promise<TeamInfo[]>;\n getUsage(startDate: string, endDate: string): Promise<UsageMetrics>;\n getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics>;\n}\n\nexport const liteLlmApiRef = createApiRef<LiteLlmApiInterface>({\n id: 'plugin.litellm.api',\n});\n\nexport class LiteLlmApi implements LiteLlmApiInterface {\n private fetchApi: FetchApi;\n private basePath: string;\n\n constructor(fetchApi: FetchApi, basePath: string = '/api/litellm') {\n this.fetchApi = fetchApi;\n this.basePath = basePath;\n }\n\n private async throwIfNotOk(response: Response): Promise<void> {\n if (!response.ok) {\n let body: unknown;\n try { body = await response.json(); } catch { body = await response.text().catch(() => ''); }\n throw new ApiError(`${response.status} ${response.statusText}`, response.status, body);\n }\n }\n\n private async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.basePath}${path}`, window.location.origin);\n if (params) {\n Object.entries(params).forEach(([key, value]) => url.searchParams.append(key, value));\n }\n const response = await this.fetchApi.fetch(url.toString());\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n private async del<T>(path: string): Promise<T> {\n const response = await this.fetchApi.fetch(`${this.basePath}${path}`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n });\n await this.throwIfNotOk(response);\n return response.json();\n }\n\n // User identity is resolved server-side from the Backstage Bearer token.\n // No user_id param needed on the frontend.\n async getUserInfo(): Promise<UserInfo> {\n return this.get<UserInfo>('/user/info');\n }\n\n async listKeys(): Promise<VirtualKey[]> {\n return this.get<VirtualKey[]>('/keys');\n }\n\n async generateKey(request: GenerateKeyRequest): Promise<GenerateKeyResponse> {\n return this.post<GenerateKeyResponse>('/keys/generate', request);\n }\n\n async updateKey(keyId: string, request: UpdateKeyRequest): Promise<VirtualKey> {\n return this.post<VirtualKey>(`/keys/${encodeURIComponent(keyId)}/update`, request);\n }\n\n async deleteKey(keyId: string): Promise<{ success: boolean }> {\n return this.del<{ success: boolean }>(`/keys/${encodeURIComponent(keyId)}`);\n }\n\n async listModels(): Promise<ModelInfo[]> {\n return this.get<ModelInfo[]>('/models');\n }\n\n async getTeams(): Promise<TeamInfo[]> {\n return this.get<TeamInfo[]>('/teams');\n }\n\n async getUsage(startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>('/usage', { start_date: startDate, end_date: endDate });\n }\n\n async getTeamUsage(teamId: string, startDate: string, endDate: string): Promise<UsageMetrics> {\n return this.get<UsageMetrics>(`/teams/${encodeURIComponent(teamId)}/usage`, {\n start_date: startDate,\n end_date: endDate,\n });\n }\n}\n", "import React from 'react';\nimport { Box, Typography, LinearProgress, Paper, Chip } from '@mui/material';\nimport { Warning } from '@mui/icons-material';\nimport { UserInfo, TeamInfo } from '../types';\n\ninterface DashboardHeaderProps {\n userInfo: UserInfo;\n teams: TeamInfo[];\n loading: boolean;\n}\n\nexport const DashboardHeader: React.FC<DashboardHeaderProps> = ({ userInfo, teams, loading }) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n const displayName = userInfo.user_email ?? userInfo.email ?? userInfo.user_id;\n const budget = userInfo.max_budget ?? 0;\n const spend = userInfo.spend ?? userInfo.current_spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n return (\n <Paper sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"flex-start\" gap={2} flexWrap=\"wrap\">\n <Box flexGrow={1}>\n <Typography variant=\"h6\">{displayName}</Typography>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {userInfo.user_id}\n </Typography>\n\n {teams.length > 0 && (\n <Box display=\"flex\" gap={0.75} flexWrap=\"wrap\" mt={1}>\n {teams.map(team => (\n <Chip\n key={team.team_id}\n label={team.team_alias || team.team_id}\n size=\"small\"\n variant=\"outlined\"\n color=\"primary\"\n />\n ))}\n </Box>\n )}\n </Box>\n\n {budget > 0 && (\n <Box minWidth={220}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip icon={<Warning />} label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 8, borderRadius: 1 }}\n />\n </Box>\n )}\n </Box>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Button,\n IconButton,\n Box,\n Typography,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n MenuItem,\n Chip,\n CircularProgress,\n Autocomplete,\n} from '@mui/material';\nimport { ContentCopy, Delete, Add, Edit, Visibility, VisibilityOff } from '@mui/icons-material';\nimport {\n VirtualKey,\n ModelInfo,\n TeamInfo,\n GenerateKeyRequest,\n GenerateKeyResponse,\n UpdateKeyRequest,\n} from '../types';\n\ninterface KeysTableProps {\n keys: VirtualKey[];\n models: ModelInfo[];\n teams: TeamInfo[];\n loading: boolean;\n onGenerateKey: (request: GenerateKeyRequest) => Promise<GenerateKeyResponse>;\n onUpdateKey: (keyId: string, request: UpdateKeyRequest) => Promise<void>;\n onDeleteKey: (keyId: string) => Promise<void>;\n}\n\nconst KEY_TYPES: { value: string; label: string }[] = [\n { value: 'llm_api', label: 'AI API' },\n { value: 'management', label: 'Management' },\n { value: 'read_only', label: 'Read Only' },\n];\n\nconst maskKey = (key: string): string => {\n if (key.length <= 8) return '***';\n return `${key.slice(0, 4)}...${key.slice(-4)}`;\n};\n\nconst formatDate = (dateStr: string): string => {\n try {\n return new Date(dateStr).toLocaleDateString();\n } catch {\n return dateStr;\n }\n};\n\nconst emptyForm = (): GenerateKeyRequest => ({\n alias: '',\n models: [],\n duration: '30d',\n max_budget: undefined,\n tpm_limit: undefined,\n team_id: undefined,\n key_type: 'llm_api',\n});\n\nconst keyToEditForm = (k: VirtualKey): UpdateKeyRequest => ({\n key_alias: k.key_alias ?? '',\n models: k.models ?? [],\n max_budget: k.max_budget,\n tpm_limit: k.tpm_limit,\n rpm_limit: k.rpm_limit,\n});\n\nexport const KeysTable: React.FC<KeysTableProps> = ({\n keys,\n models,\n teams,\n loading,\n onGenerateKey,\n onUpdateKey,\n onDeleteKey,\n}) => {\n const [generateModalOpen, setGenerateModalOpen] = useState(false);\n const [showKeyValue, setShowKeyValue] = useState<string | null>(null);\n const [newKeyValue, setNewKeyValue] = useState<string | null>(null);\n const [formData, setFormData] = useState<GenerateKeyRequest>(emptyForm());\n const [submitting, setSubmitting] = useState(false);\n\n const [editingKey, setEditingKey] = useState<VirtualKey | null>(null);\n const [editForm, setEditForm] = useState<UpdateKeyRequest>({});\n const [editSubmitting, setEditSubmitting] = useState(false);\n\n const selectedModels = models.filter(m => (formData.models || []).includes(m.model_name));\n const selectedTeam = teams.find(t => t.team_id === formData.team_id) ?? null;\n\n const editSelectedModels = models.filter(m => (editForm.models || []).includes(m.model_name));\n\n const handleGenerate = async () => {\n setSubmitting(true);\n try {\n const response = await onGenerateKey(formData);\n setNewKeyValue(response.key);\n setFormData(emptyForm());\n } catch (error) {\n console.error('Failed to generate key:', error);\n } finally {\n setSubmitting(false);\n }\n };\n\n const handleCloseModal = () => {\n setGenerateModalOpen(false);\n setNewKeyValue(null);\n setFormData(emptyForm());\n };\n\n const handleOpenEdit = (k: VirtualKey) => {\n setEditingKey(k);\n setEditForm(keyToEditForm(k));\n };\n\n const handleCloseEdit = () => {\n setEditingKey(null);\n setEditForm({});\n };\n\n const handleUpdate = async () => {\n if (!editingKey) return;\n setEditSubmitting(true);\n try {\n await onUpdateKey(editingKey.key, editForm);\n handleCloseEdit();\n } catch (error) {\n console.error('Failed to update key:', error);\n } finally {\n setEditSubmitting(false);\n }\n };\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text);\n };\n\n return (\n <>\n <Paper sx={{ mb: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" p={2}>\n <Typography variant=\"h6\">Virtual Keys</Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<Add />}\n onClick={() => setGenerateModalOpen(true)}\n >\n Generate New Key\n </Button>\n </Box>\n\n <TableContainer>\n <Table>\n <TableHead>\n <TableRow>\n <TableCell>Alias</TableCell>\n <TableCell>Key</TableCell>\n <TableCell>Created</TableCell>\n <TableCell>Spend</TableCell>\n <TableCell>Budget</TableCell>\n <TableCell>TPM Limit</TableCell>\n <TableCell>Models</TableCell>\n <TableCell align=\"right\">Actions</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {loading ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <CircularProgress size={24} />\n </TableCell>\n </TableRow>\n ) : keys.length === 0 ? (\n <TableRow>\n <TableCell colSpan={8} align=\"center\">\n <Typography color=\"text.secondary\">No keys found</Typography>\n </TableCell>\n </TableRow>\n ) : (\n keys.map((key) => (\n <TableRow key={key.key}>\n <TableCell>{key.key_alias || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" alignItems=\"center\" gap={0.5}>\n <Typography variant=\"body2\" component=\"code\" sx={{ fontFamily: 'monospace' }}>\n {showKeyValue === key.key ? key.key : maskKey(key.key)}\n </Typography>\n <IconButton\n size=\"small\"\n onClick={() => setShowKeyValue(showKeyValue === key.key ? null : key.key)}\n >\n {showKeyValue === key.key ? <VisibilityOff /> : <Visibility />}\n </IconButton>\n <IconButton size=\"small\" onClick={() => copyToClipboard(key.key)}>\n <ContentCopy fontSize=\"small\" />\n </IconButton>\n </Box>\n </TableCell>\n <TableCell>{formatDate(key.created_at)}</TableCell>\n <TableCell>${key.spend?.toFixed(4) || '0.00'}</TableCell>\n <TableCell>{key.max_budget ? `$${key.max_budget}` : '-'}</TableCell>\n <TableCell>{key.tpm_limit || '-'}</TableCell>\n <TableCell>\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\">\n {key.models?.slice(0, 2).map((model) => (\n <Chip key={model} label={model} size=\"small\" />\n ))}\n {(key.models?.length || 0) > 2 && (\n <Chip label={`+${(key.models?.length || 0) - 2}`} size=\"small\" variant=\"outlined\" />\n )}\n </Box>\n </TableCell>\n <TableCell align=\"right\">\n <IconButton onClick={() => handleOpenEdit(key)}>\n <Edit fontSize=\"small\" />\n </IconButton>\n <IconButton color=\"error\" onClick={() => onDeleteKey(key.key)}>\n <Delete />\n </IconButton>\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </TableContainer>\n </Paper>\n\n <Dialog open={generateModalOpen} onClose={handleCloseModal} maxWidth=\"sm\" fullWidth>\n <DialogTitle>{newKeyValue ? 'Key Generated' : 'Generate New Key'}</DialogTitle>\n <DialogContent>\n {newKeyValue ? (\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\" gutterBottom>\n Copy this key now. You won't be able to see it again.\n </Typography>\n <Box\n display=\"flex\"\n alignItems=\"center\"\n gap={1}\n mt={2}\n p={2}\n sx={{ backgroundColor: 'action.hover', borderRadius: 1 }}\n >\n <Typography\n component=\"code\"\n sx={{ fontFamily: 'monospace', wordBreak: 'break-all', flex: 1 }}\n >\n {newKeyValue}\n </Typography>\n <IconButton onClick={() => copyToClipboard(newKeyValue)}>\n <ContentCopy />\n </IconButton>\n </Box>\n </Box>\n ) : (\n <Box display=\"flex\" flexDirection=\"column\" gap={2} mt={1}>\n <TextField\n label=\"Alias\"\n value={formData.alias || ''}\n onChange={(e) => setFormData({ ...formData, alias: e.target.value })}\n fullWidth\n />\n <TextField\n select\n label=\"Key Type\"\n value={formData.key_type || 'llm_api'}\n onChange={(e) => setFormData({ ...formData, key_type: e.target.value })}\n fullWidth\n >\n {KEY_TYPES.map(kt => (\n <MenuItem key={kt.value} value={kt.value}>{kt.label}</MenuItem>\n ))}\n </TextField>\n <TextField\n select\n label=\"Duration\"\n value={formData.duration || '30d'}\n onChange={(e) => setFormData({ ...formData, duration: e.target.value })}\n fullWidth\n >\n <MenuItem value=\"1d\">1 Day</MenuItem>\n <MenuItem value=\"7d\">7 Days</MenuItem>\n <MenuItem value=\"30d\">30 Days</MenuItem>\n <MenuItem value=\"90d\">90 Days</MenuItem>\n <MenuItem value=\"1y\">1 Year</MenuItem>\n </TextField>\n\n {teams.length > 0 && (\n <Autocomplete\n options={teams}\n getOptionLabel={t => t.team_alias || t.team_id}\n value={selectedTeam}\n onChange={(_e, team) =>\n setFormData({ ...formData, team_id: team?.team_id })\n }\n renderInput={params => (\n <TextField {...params} label=\"Team (optional)\" fullWidth />\n )}\n />\n )}\n\n {models.length > 0 && (\n <Autocomplete\n multiple\n options={models}\n groupBy={m => m.mode || 'other'}\n getOptionLabel={m => m.model_name}\n value={selectedModels}\n onChange={(_e, selected) =>\n setFormData({ ...formData, models: selected.map(m => m.model_name) })\n }\n renderOption={(props, m) => (\n <li {...props}>\n {m.model_name}\n {m.supports_function_calling && ' \uD83D\uDD27'}\n {m.supports_vision && ' \uD83D\uDC41\uFE0F'}\n </li>\n )}\n renderInput={params => (\n <TextField {...params} label=\"Models\" fullWidth />\n )}\n />\n )}\n\n <TextField\n label=\"Max Budget (USD)\"\n type=\"number\"\n value={formData.max_budget ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"TPM Limit\"\n type=\"number\"\n value={formData.tpm_limit ?? ''}\n onChange={(e) =>\n setFormData({ ...formData, tpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseModal}>{newKeyValue ? 'Done' : 'Cancel'}</Button>\n {!newKeyValue && (\n <Button onClick={handleGenerate} variant=\"contained\" color=\"primary\" disabled={submitting}>\n {submitting ? <CircularProgress size={24} /> : 'Generate'}\n </Button>\n )}\n </DialogActions>\n </Dialog>\n\n <Dialog open={!!editingKey} onClose={handleCloseEdit} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Edit Key</DialogTitle>\n <DialogContent>\n {editingKey && (\n <Box display=\"flex\" flexDirection=\"column\" gap={2} mt={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n <code style={{ fontFamily: 'monospace' }}>{maskKey(editingKey.key)}</code>\n </Typography>\n <TextField\n label=\"Alias\"\n value={editForm.key_alias || ''}\n onChange={(e) => setEditForm({ ...editForm, key_alias: e.target.value })}\n fullWidth\n />\n\n {models.length > 0 && (\n <Autocomplete\n multiple\n options={models}\n groupBy={m => m.mode || 'other'}\n getOptionLabel={m => m.model_name}\n value={editSelectedModels}\n onChange={(_e, selected) =>\n setEditForm({ ...editForm, models: selected.map(m => m.model_name) })\n }\n renderInput={params => (\n <TextField {...params} label=\"Models\" fullWidth />\n )}\n />\n )}\n\n <TextField\n label=\"Max Budget (USD)\"\n type=\"number\"\n value={editForm.max_budget ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, max_budget: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"TPM Limit\"\n type=\"number\"\n value={editForm.tpm_limit ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, tpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n <TextField\n label=\"RPM Limit\"\n type=\"number\"\n value={editForm.rpm_limit ?? ''}\n onChange={(e) =>\n setEditForm({ ...editForm, rpm_limit: e.target.value ? Number(e.target.value) : undefined })\n }\n fullWidth\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseEdit}>Cancel</Button>\n <Button onClick={handleUpdate} variant=\"contained\" color=\"primary\" disabled={editSubmitting}>\n {editSubmitting ? <CircularProgress size={24} /> : 'Save'}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n CircularProgress,\n} from '@mui/material';\nimport {\n AreaChart,\n Area,\n BarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { DateRange, UsageMetrics, ModelInfo } from '../types';\n\ninterface UsageStatsProps {\n usage: UsageMetrics | null;\n models: ModelInfo[];\n dateRange: DateRange;\n onDateRangeChange: (range: DateRange) => void;\n loading: boolean;\n}\n\ntype DatePreset = 'today' | '7d' | '30d';\n\nexport const UsageStats: React.FC<UsageStatsProps> = ({\n usage,\n models,\n dateRange,\n onDateRangeChange,\n loading,\n}) => {\n const [selectedPreset, setSelectedPreset] = useState<DatePreset>('7d');\n const [selectedModel, setSelectedModel] = useState<string>('all');\n\n const handlePresetChange = (preset: DatePreset) => {\n setSelectedPreset(preset);\n const end = new Date();\n const start = new Date();\n if (preset === 'today') start.setHours(0, 0, 0, 0);\n else if (preset === '7d') start.setDate(start.getDate() - 7);\n else if (preset === '30d') start.setDate(start.getDate() - 30);\n onDateRangeChange({ start, end });\n };\n\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"center\" p={4}>\n <CircularProgress />\n </Box>\n </Paper>\n );\n }\n\n const dailyData =\n usage?.daily_usage?.map((d) => ({\n date: d.date,\n spend: d.spend,\n promptTokens: d.prompt_tokens,\n completionTokens: d.completion_tokens,\n totalTokens: d.total_tokens,\n })) || [];\n\n const usageByModelData = Object.entries(usage?.usage_by_model || {}).map(([model, data]) => ({\n model,\n spend: data.total_spend,\n promptTokens: data.prompt_tokens,\n completionTokens: data.completion_tokens,\n }));\n\n const filteredModelData =\n selectedModel === 'all'\n ? usageByModelData\n : usageByModelData.filter((d) => d.model === selectedModel);\n\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={3}>\n <Typography variant=\"h6\">Usage Analytics</Typography>\n <Box display=\"flex\" gap={2} alignItems=\"center\">\n <FormControl size=\"small\" sx={{ minWidth: 140 }}>\n <InputLabel>Period</InputLabel>\n <Select\n value={selectedPreset}\n label=\"Period\"\n onChange={(e) => handlePresetChange(e.target.value as DatePreset)}\n >\n <MenuItem value=\"today\">Today</MenuItem>\n <MenuItem value=\"7d\">Last 7 days</MenuItem>\n <MenuItem value=\"30d\">Last 30 days</MenuItem>\n </Select>\n </FormControl>\n <FormControl size=\"small\" sx={{ minWidth: 150 }}>\n <InputLabel>Model</InputLabel>\n <Select\n value={selectedModel}\n label=\"Model\"\n onChange={(e) => setSelectedModel(e.target.value)}\n >\n <MenuItem value=\"all\">All Models</MenuItem>\n {models.map((m) => (\n <MenuItem key={m.model_name} value={m.model_name}>\n {m.model_name}\n </MenuItem>\n ))}\n </Select>\n </FormControl>\n </Box>\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 12 }} />\n <YAxis tick={{ fontSize: 12 }} tickFormatter={(v) => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(value: number) => [`$${value.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Token Usage by Model\n </Typography>\n <Box height={250}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <BarChart data={filteredModelData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"model\" tick={{ fontSize: 10 }} />\n <YAxis tick={{ fontSize: 12 }} />\n <Tooltip />\n <Legend />\n <Bar dataKey=\"promptTokens\" name=\"Prompt Tokens\" fill=\"#8884d8\" stackId=\"a\" />\n <Bar dataKey=\"completionTokens\" name=\"Completion Tokens\" fill=\"#82ca9d\" stackId=\"a\" />\n </BarChart>\n </ResponsiveContainer>\n </Box>\n </Grid>\n\n <Grid item xs={12}>\n <Box display=\"flex\" gap={4} flexWrap=\"wrap\">\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Spend</Typography>\n <Typography variant=\"h5\">${usage?.total_spend?.toFixed(4) || '0.00'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Total Tokens</Typography>\n <Typography variant=\"h5\">{usage?.total_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Prompt Tokens</Typography>\n <Typography variant=\"h5\">{usage?.prompt_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n <Box>\n <Typography variant=\"body2\" color=\"text.secondary\">Completion Tokens</Typography>\n <Typography variant=\"h5\">{usage?.completion_tokens?.toLocaleString() || '0'}</Typography>\n </Box>\n </Box>\n </Grid>\n </Grid>\n </Paper>\n );\n};\n", "import React, { useState } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n LinearProgress,\n Chip,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n TableContainer,\n Collapse,\n IconButton,\n CircularProgress,\n} from '@mui/material';\nimport { ExpandMore, ExpandLess, Group } from '@mui/icons-material';\nimport {\n AreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n} from 'recharts';\nimport { TeamInfo, UsageMetrics, DateRange } from '../types';\n\ninterface TeamCardProps {\n team: TeamInfo;\n usage: UsageMetrics | null;\n usageLoading: boolean;\n}\n\nconst TeamCard: React.FC<TeamCardProps> = ({ team, usage, usageLoading }) => {\n const [expanded, setExpanded] = useState(false);\n\n const budget = team.max_budget ?? 0;\n const spend = team.spend ?? 0;\n const budgetPct = budget > 0 ? Math.min((spend / budget) * 100, 100) : 0;\n const isOver = budget > 0 && spend >= budget;\n const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;\n\n const dailyData = usage?.daily_usage?.map(d => ({ date: d.date, spend: d.spend })) ?? [];\n\n return (\n <Paper variant=\"outlined\" sx={{ p: 2, mb: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"action\" />\n <Box flexGrow={1}>\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {team.team_alias ?? team.team_id}\n </Typography>\n {team.models?.length ? (\n <Box display=\"flex\" gap={0.5} flexWrap=\"wrap\" mt={0.5}>\n {team.models.map(m => (\n <Chip key={m} label={m} size=\"small\" variant=\"outlined\" />\n ))}\n </Box>\n ) : (\n <Typography variant=\"caption\" color=\"text.secondary\">All models</Typography>\n )}\n </Box>\n <IconButton size=\"small\" onClick={() => setExpanded(e => !e)}>\n {expanded ? <ExpandLess /> : <ExpandMore />}\n </IconButton>\n </Box>\n\n {budget > 0 && (\n <Box mt={1.5}>\n <Box display=\"flex\" justifyContent=\"space-between\" mb={0.5}>\n <Typography variant=\"body2\">\n ${spend.toFixed(2)} / ${budget.toFixed(2)}\n </Typography>\n {isOver && <Chip label=\"Over Budget\" size=\"small\" color=\"error\" />}\n {isNear && <Chip label=\"Near Limit\" size=\"small\" color=\"warning\" />}\n </Box>\n <LinearProgress\n variant=\"determinate\"\n value={budgetPct}\n color={isOver ? 'error' : isNear ? 'warning' : 'primary'}\n sx={{ height: 6, borderRadius: 1 }}\n />\n </Box>\n )}\n\n <Collapse in={expanded}>\n <Box mt={2}>\n {usageLoading ? (\n <Box display=\"flex\" justifyContent=\"center\" p={2}>\n <CircularProgress size={24} />\n </Box>\n ) : dailyData.length > 0 ? (\n <>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Daily Spend\n </Typography>\n <Box height={160}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData}>\n <CartesianGrid strokeDasharray=\"3 3\" />\n <XAxis dataKey=\"date\" tick={{ fontSize: 11 }} />\n <YAxis tick={{ fontSize: 11 }} tickFormatter={v => `$${v.toFixed(2)}`} />\n <Tooltip formatter={(v: number) => [`$${v.toFixed(4)}`, 'Spend']} />\n <Area type=\"monotone\" dataKey=\"spend\" stroke=\"#8884d8\" fill=\"#8884d8\" fillOpacity={0.3} />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n </>\n ) : null}\n\n {team.members_with_roles?.length ? (\n <Box mt={2}>\n <Typography variant=\"subtitle2\" color=\"text.secondary\" gutterBottom>\n Members\n </Typography>\n <TableContainer>\n <Table size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>User</TableCell>\n <TableCell>Role</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {team.members_with_roles.map(m => (\n <TableRow key={m.user_id}>\n <TableCell sx={{ fontFamily: 'monospace', fontSize: 12 }}>{m.user_id}</TableCell>\n <TableCell>\n <Chip\n label={m.role}\n size=\"small\"\n color={m.role === 'admin' ? 'primary' : 'default'}\n />\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n ) : null}\n </Box>\n </Collapse>\n </Paper>\n );\n};\n\ninterface TeamUsageProps {\n teams: TeamInfo[];\n loading: boolean;\n dateRange: DateRange;\n getTeamUsage: (teamId: string) => UsageMetrics | null;\n getTeamUsageLoading: (teamId: string) => boolean;\n}\n\nexport const TeamUsage: React.FC<TeamUsageProps> = ({\n teams,\n loading,\n getTeamUsage,\n getTeamUsageLoading,\n}) => {\n if (loading) {\n return (\n <Paper sx={{ p: 2 }}>\n <LinearProgress />\n </Paper>\n );\n }\n\n if (!teams.length) {\n return (\n <Paper sx={{ p: 2 }}>\n <Box display=\"flex\" alignItems=\"center\" gap={1}>\n <Group color=\"disabled\" />\n <Typography color=\"text.secondary\" variant=\"body2\">\n No team membership found in LiteLLM for this account.\n </Typography>\n </Box>\n </Paper>\n );\n }\n\n return (\n <Box>\n <Typography variant=\"h6\" mb={1}>Teams</Typography>\n {teams.map(team => (\n <TeamCard\n key={team.team_id}\n team={team}\n usage={getTeamUsage(team.team_id)}\n usageLoading={getTeamUsageLoading(team.team_id)}\n />\n ))}\n </Box>\n );\n};\n", "import React, { useState, useCallback, useMemo } from 'react';\nimport { Grid, Box, Snackbar, Alert, CircularProgress, Typography, Paper } from '@mui/material';\nimport { useAsync, useAsyncRetry } from 'react-use';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { DashboardHeader } from './DashboardHeader';\nimport { KeysTable } from './KeysTable';\nimport { UsageStats } from './UsageStats';\nimport { TeamUsage } from './TeamUsage';\nimport { liteLlmApiRef } from '../api';\nimport { DateRange, GenerateKeyRequest, GenerateKeyResponse, UpdateKeyRequest, UsageMetrics } from '../types';\n\nexport const LiteLLMPage: React.FC = () => {\n const api = useApi(liteLlmApiRef);\n\n const [dateRange, setDateRange] = useState<DateRange>(() => {\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 7);\n return { start, end };\n });\n\n const [snackbar, setSnackbar] = useState<{ message: string; severity: 'success' | 'error' } | null>(null);\n\n // Team usage cache: teamId -> UsageMetrics\n const [teamUsageCache, setTeamUsageCache] = useState<Record<string, UsageMetrics | null>>({});\n const [teamUsageLoading, setTeamUsageLoading] = useState<Record<string, boolean>>({});\n\n const { value: userInfo, loading: userLoading, error: userError } = useAsync(\n () => api.getUserInfo(),\n [api],\n );\n\n const { value: keys, loading: keysLoading, retry: refreshKeys } = useAsyncRetry(\n async () => {\n try {\n return await api.listKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to load keys: ${e.message}`, severity: 'error' });\n return [];\n }\n },\n [api],\n );\n\n const { value: allModels, loading: modelsLoading } = useAsync(\n () => api.listModels().catch(() => []),\n [api],\n );\n\n const { value: allTeams, loading: teamsLoading } = useAsync(\n () => api.getTeams().catch(() => []),\n [api],\n );\n\n // Filter to teams the current user belongs to\n const teams = useMemo(() => {\n if (!allTeams?.length) return [];\n if (!userInfo) return allTeams;\n const userId = userInfo.user_id;\n if (userInfo.teams?.length) {\n return allTeams.filter(t => userInfo.teams!.includes(t.team_id));\n }\n const byMembership = allTeams.filter(t =>\n t.members_with_roles?.some(m => m.user_id === userId),\n );\n return byMembership.length > 0 ? byMembership : allTeams;\n }, [allTeams, userInfo]);\n\n const { value: usage, loading: usageLoading } = useAsync(async () => {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n return api.getUsage(startDate, endDate);\n }, [api, dateRange]);\n\n // Fetch team usage on demand when a team card is expanded\n const loadTeamUsage = useCallback(async (teamId: string) => {\n if (teamUsageCache[teamId] !== undefined || teamUsageLoading[teamId]) return;\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: true }));\n try {\n const startDate = dateRange.start.toISOString().split('T')[0];\n const endDate = dateRange.end.toISOString().split('T')[0];\n const data = await api.getTeamUsage(teamId, startDate, endDate);\n setTeamUsageCache(prev => ({ ...prev, [teamId]: data }));\n } catch {\n setTeamUsageCache(prev => ({ ...prev, [teamId]: null }));\n } finally {\n setTeamUsageLoading(prev => ({ ...prev, [teamId]: false }));\n }\n }, [api, dateRange, teamUsageCache, teamUsageLoading]);\n\n // Models available for key generation: intersection of all models with user-level\n // and team-level restrictions. If a user has no model restrictions, all models are allowed.\n const allowedModels = useMemo(() => {\n if (!allModels?.length) return [];\n const userModels = userInfo?.models;\n const teamModels = teams?.flatMap(t => t.models ?? []);\n const hasUserRestriction = userModels && userModels.length > 0;\n const hasTeamRestriction = teamModels && teamModels.length > 0;\n if (!hasUserRestriction && !hasTeamRestriction) return allModels;\n const allowed = new Set([\n ...(hasUserRestriction ? userModels! : allModels.map(m => m.model_name)),\n ...(hasTeamRestriction ? teamModels! : []),\n ]);\n return allModels.filter(m => allowed.has(m.model_name));\n }, [allModels, userInfo, teams]);\n\n const handleGenerateKey = useCallback(\n async (request: GenerateKeyRequest): Promise<GenerateKeyResponse> => {\n const response = await api.generateKey(request);\n setSnackbar({ message: 'Key generated successfully', severity: 'success' });\n refreshKeys();\n return response;\n },\n [api, refreshKeys],\n );\n\n const handleUpdateKey = useCallback(\n async (keyId: string, request: UpdateKeyRequest) => {\n try {\n await api.updateKey(keyId, request);\n setSnackbar({ message: 'Key updated successfully', severity: 'success' });\n refreshKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to update key: ${e.message}`, severity: 'error' });\n throw e;\n }\n },\n [api, refreshKeys],\n );\n\n const handleDeleteKey = useCallback(\n async (keyId: string) => {\n try {\n await api.deleteKey(keyId);\n setSnackbar({ message: 'Key revoked successfully', severity: 'success' });\n refreshKeys();\n } catch (e: any) {\n setSnackbar({ message: `Failed to revoke key: ${e.message}`, severity: 'error' });\n }\n },\n [api, refreshKeys],\n );\n\n const isInitialLoading = userLoading && !userInfo;\n\n if (isInitialLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight=\"50vh\">\n <CircularProgress />\n </Box>\n );\n }\n\n // User exists in Backstage but has no LiteLLM account\n if (userError || !userInfo) {\n const isProvisioningEnabled = (userError as any)?.body?.provisioning === true;\n const hint = (userError as any)?.body?.hint;\n return (\n <Box p={3}>\n <Paper sx={{ p: 3 }}>\n <Typography variant=\"h6\" gutterBottom>Account not provisioned</Typography>\n <Typography color=\"text.secondary\" paragraph>\n Your Backstage account is not linked to a LiteLLM user.\n </Typography>\n {hint ? (\n <Typography variant=\"body2\" color=\"text.secondary\">{hint}</Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {isProvisioningEnabled\n ? 'Auto-provisioning is enabled but failed. Check the backend logs.'\n : 'Set litellm.provisioning.enabled: true in app-config.yaml to enable auto-provisioning, or ask your administrator to create the account manually.'}\n </Typography>\n )}\n </Paper>\n </Box>\n );\n }\n\n return (\n <Box p={3}>\n <Grid container spacing={2}>\n <Grid item xs={12}>\n <DashboardHeader userInfo={userInfo} teams={teams ?? []} loading={userLoading || teamsLoading} />\n </Grid>\n\n <Grid item xs={12}>\n <TeamUsage\n teams={teams ?? []}\n loading={teamsLoading}\n dateRange={dateRange}\n getTeamUsage={teamId => {\n if (teamUsageCache[teamId] === undefined) loadTeamUsage(teamId);\n return teamUsageCache[teamId] ?? null;\n }}\n getTeamUsageLoading={teamId => teamUsageLoading[teamId] ?? false}\n />\n </Grid>\n\n <Grid item xs={12}>\n <KeysTable\n keys={keys ?? []}\n models={allowedModels}\n teams={teams ?? []}\n loading={keysLoading || modelsLoading}\n onGenerateKey={handleGenerateKey}\n onUpdateKey={handleUpdateKey}\n onDeleteKey={handleDeleteKey}\n />\n </Grid>\n\n <Grid item xs={12}>\n <UsageStats\n usage={usage ?? null}\n models={allModels ?? []}\n dateRange={dateRange}\n onDateRangeChange={setDateRange}\n loading={usageLoading}\n />\n </Grid>\n </Grid>\n\n <Snackbar\n open={!!snackbar}\n autoHideDuration={5000}\n onClose={() => setSnackbar(null)}\n anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}\n >\n {snackbar ? (\n <Alert severity={snackbar.severity} onClose={() => setSnackbar(null)}>\n {snackbar.message}\n </Alert>\n ) : undefined}\n </Snackbar>\n </Box>\n );\n};\n", "import React from 'react';\nimport { TrendingUp as TrendingUpIcon } from '@mui/icons-material';\nimport {\n createFrontendPlugin,\n createApiExtension,\n createPageExtension,\n createApiFactory,\n createSidebarExtension,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { liteLlmApiRef, LiteLlmApi } from './api';\n\nexport const litellmPlugin = createFrontendPlugin({\n id: 'litellm',\n extensions: [\n createApiExtension({\n factory: createApiFactory({\n api: liteLlmApiRef,\n deps: { fetchApi: fetchApiRef },\n factory: ({ fetchApi }) => new LiteLlmApi(fetchApi),\n }),\n }),\n createSidebarExtension({\n id: 'root',\n title: 'LiteLLM',\n icon: <TrendingUpIcon />,\n defaultPath: '/litellm',\n }),\n createPageExtension({\n defaultPath: '/litellm',\n loader: async () => {\n const { LiteLLMPage } = await import('./components/LiteLLMPage');\n return React.createElement(LiteLLMPage);\n },\n }),\n ],\n});\n", "export { litellmPlugin } from './plugin';\nexport { LiteLLMPage } from './components/LiteLLMPage';\nexport { DashboardHeader } from './components/DashboardHeader';\nexport { KeysTable } from './components/KeysTable';\nexport { UsageStats } from './components/UsageStats';\nexport { TeamUsage } from './components/TeamUsage';\nexport { LiteLlmApi, liteLlmApiRef } from './api';\nexport type { LiteLlmApiInterface } from './api';\nexport * from './types';\n"],
5
- "mappings": "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",
6
- "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.10",
3
+ "version": "0.1.12",
4
4
  "backstage": {
5
5
  "role": "frontend-plugin",
6
6
  "pluginId": "litellm",