@acarmisc/backstage-plugin-litellm 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 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/index.ts", "../src/plugin.tsx", "../src/components/LiteLLMHomeWidget.tsx"],
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 canGenerate = true; // Always allow generation regardless of team selection\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 // Close dialog after showing the generated key\n setTimeout(() => {\n setGenerateModalOpen(false);\n setNewKeyValue(null);\n }, 1500);\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 \n variant=\"body2\" \n component=\"code\" \n color=\"primary\"\n sx={{ \n fontFamily: 'monospace', \n backgroundColor: 'background.default', \n px: 1, \n py: 0.5, \n borderRadius: 1,\n maxWidth: '250px',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\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={{\n backgroundColor: 'grey.100',\n borderRadius: 1,\n }}\n >\n <Typography\n component=\"code\"\n color=\"text.primary\"\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 helperText=\"Optional: bind this key to a specific team for scoped access\"\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 {newKeyValue && (\n <Button onClick={handleCloseModal} variant=\"contained\" color=\"success\">\n Done\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', color: 'inherit' }}>{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 [selectedModel, setSelectedModel] = useState<string>('all');\n const [tab, setTab] = useState<TabKey>('costs');\n\n // Derive selected preset from dateRange to keep it in sync\n // Check order matters: today (0 days), then 7d (1-7 days), then 30d (8+ days)\n const selectedPreset = useMemo(() => {\n const start = dateRange.start;\n const end = dateRange.end;\n // Same day = exactly 'today' (diffDays = 0)\n // If user picks 1 day range (yesterday-today, etc.), that's a 7-day period\n if (start.toDateString() === end.toDateString()) return 'today';\n const diffMs = end.getTime() - start.getTime();\n const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24));\n if (diffDays <= 7) return '7d';\n return '30d';\n }, [dateRange]);\n\n const handlePresetChange = (preset: DatePreset) => {\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' | 'warning' | '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({ \n message: 'Key revoked successfully', \n severity: 'success' \n });\n refreshKeys();\n } catch (e: any) {\n // Handle \"already deleted\" response gracefully (idempotent)\n // If backend returns 200 with success=true, it means key was already gone\n if (e.body?.success && (e.body?.message?.includes('already deleted') || e.body?.message?.includes('never existed'))) {\n setSnackbar({ \n message: 'Key was already deleted', \n severity: 'warning' \n });\n refreshKeys(); // Still refresh to ensure UI consistency\n return;\n }\n setSnackbar({ \n message: `Failed to revoke key: ${e.message}`, \n severity: 'error' \n });\n } finally {\n refreshKeys();\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", "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 { LiteLLMHomeWidget } from './components/LiteLLMHomeWidget';\nexport type { LiteLLMHomeWidgetProps } from './components/LiteLLMHomeWidget';\nexport { LiteLlmApi, liteLlmApiRef } from './api';\nexport type { LiteLlmApiInterface } from './api';\nexport * from './types';\n", "import React from 'react';\nimport { TrendingUp as TrendingUpIcon } from '@mui/icons-material';\nimport {\n createFrontendPlugin,\n ApiBlueprint,\n PageBlueprint,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { liteLlmApiRef, LiteLlmApi } from './api';\n\nconst liteLlmApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: liteLlmApiRef,\n deps: { fetchApi: fetchApiRef },\n factory: ({ fetchApi }) => new LiteLlmApi(fetchApi),\n }),\n});\n\nconst liteLlmPage = PageBlueprint.make({\n params: {\n path: '/litellm',\n title: 'LiteLLM',\n icon: <TrendingUpIcon />,\n loader: async () => {\n const { LiteLLMPage } = await import('./components/LiteLLMPage');\n return <LiteLLMPage />;\n },\n },\n});\n\nexport const litellmPlugin = createFrontendPlugin({\n pluginId: 'litellm',\n extensions: [liteLlmApi, liteLlmPage],\n});\n", "import React, { useState, useEffect } from 'react';\nimport {\n Paper,\n Box,\n Typography,\n FormControl,\n Select,\n MenuItem,\n Grid,\n CircularProgress,\n Alert,\n} from '@mui/material';\nimport { AreaChart, Area, ResponsiveContainer } from 'recharts';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { liteLlmApiRef } from '../api';\nimport { UsageMetrics, VirtualKey } from '../types';\n\nexport interface LiteLLMHomeWidgetProps {\n /** Default period when the widget mounts. Defaults to '7d'. */\n defaultPeriod?: 'today' | '7d' | '30d';\n /** Optional title override. Defaults to 'LiteLLM Usage'. */\n title?: string;\n}\n\ntype DatePreset = 'today' | '7d' | '30d';\n\nconst fmtUsd = (n: number) => `$${(n ?? 0).toFixed(n < 1 ? 4 : 2)}`;\nconst fmtInt = (n: number) => (n ?? 0).toLocaleString();\n\nfunction presetToDateRange(preset: DatePreset): { start: Date; end: Date } {\n const end = new Date();\n const start = new Date();\n if (preset === 'today') {\n start.setHours(0, 0, 0, 0);\n } else if (preset === '7d') {\n start.setDate(start.getDate() - 7);\n } else {\n start.setDate(start.getDate() - 30);\n }\n return { start, end };\n}\n\ninterface KpiProps {\n label: string;\n value: string;\n}\n\nconst Kpi: React.FC<KpiProps> = ({ label, value }) => (\n <Box>\n <Typography variant=\"caption\" color=\"text.secondary\" display=\"block\">\n {label}\n </Typography>\n <Typography variant=\"subtitle1\" fontWeight={600}>\n {value}\n </Typography>\n </Box>\n);\n\nexport const LiteLLMHomeWidget: React.FC<LiteLLMHomeWidgetProps> = ({\n defaultPeriod = '7d',\n title = 'LiteLLM Usage',\n}) => {\n const api = useApi(liteLlmApiRef);\n const [period, setPeriod] = useState<DatePreset>(defaultPeriod);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [usage, setUsage] = useState<UsageMetrics | null>(null);\n const [keys, setKeys] = useState<VirtualKey[]>([]);\n\n useEffect(() => {\n let cancelled = false;\n setLoading(true);\n setError(null);\n\n const { start, end } = presetToDateRange(period);\n const startDate = start.toISOString().split('T')[0];\n const endDate = end.toISOString().split('T')[0];\n\n Promise.all([api.getUsage(startDate, endDate), api.listKeys()])\n .then(([usageData, keysData]) => {\n if (!cancelled) {\n setUsage(usageData);\n setKeys(keysData);\n setLoading(false);\n }\n })\n .catch((err: Error) => {\n if (!cancelled) {\n setError(err.message ?? 'Failed to load usage data');\n setLoading(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [api, period]);\n\n const dailyData = (usage?.daily_usage ?? []).map(d => ({\n date: d.date,\n spend: d.spend,\n }));\n\n const hasSparkline = dailyData.length > 0;\n\n return (\n <Paper sx={{ p: 2 }}>\n {/* Card header */}\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1.5}>\n <Typography variant=\"h6\">{title}</Typography>\n <FormControl size=\"small\" sx={{ minWidth: 90 }}>\n <Select\n value={period}\n onChange={e => setPeriod(e.target.value as DatePreset)}\n displayEmpty\n >\n <MenuItem value=\"today\">Today</MenuItem>\n <MenuItem value=\"7d\">7d</MenuItem>\n <MenuItem value=\"30d\">30d</MenuItem>\n </Select>\n </FormControl>\n </Box>\n\n {/* Loading state */}\n {loading && (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" minHeight={120}>\n <CircularProgress size={32} />\n </Box>\n )}\n\n {/* Error state */}\n {!loading && error && (\n <Alert severity=\"error\" sx={{ mt: 1 }}>\n {error}\n </Alert>\n )}\n\n {/* Content */}\n {!loading && !error && (\n <>\n <Grid container spacing={2} sx={{ mb: hasSparkline ? 1.5 : 0 }}>\n <Grid item xs={6}>\n <Kpi label=\"USD Spent\" value={fmtUsd(usage?.total_spend ?? 0)} />\n </Grid>\n <Grid item xs={6}>\n <Kpi label=\"Tokens In\" value={fmtInt(usage?.prompt_tokens ?? 0)} />\n </Grid>\n <Grid item xs={6}>\n <Kpi label=\"Tokens Out\" value={fmtInt(usage?.completion_tokens ?? 0)} />\n </Grid>\n <Grid item xs={6}>\n <Kpi label=\"Keys\" value={fmtInt(keys.length)} />\n </Grid>\n </Grid>\n\n {/* Sparkline */}\n {hasSparkline && (\n <Box height={120}>\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <AreaChart data={dailyData} margin={{ top: 4, right: 0, bottom: 0, left: 0 }}>\n <Area\n type=\"monotone\"\n dataKey=\"spend\"\n stroke=\"#8884d8\"\n fill=\"#8884d8\"\n fillOpacity={0.3}\n dot={false}\n isAnimationActive={false}\n />\n </AreaChart>\n </ResponsiveContainer>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["React", "import_react", "import_material", "import_icons_material", "React", "import_react", "import_material", "React", "import_react", "import_material", "import_icons_material", "import_recharts", "React", "import_react", "import_material", "import_core_plugin_api", "React", "import_react", "import_icons_material", "React", "TrendingUpIcon", "LiteLLMPage", "import_react", "import_material", "import_recharts", "import_core_plugin_api", "fmtUsd", "fmtInt", "React"]
7
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,9 @@ export { LiteLLMPage } from './components/LiteLLMPage';
3
3
  export { DashboardHeader } from './components/DashboardHeader';
4
4
  export { KeysTable } from './components/KeysTable';
5
5
  export { UsageStats } from './components/UsageStats';
6
+ export { TeamUsage } from './components/TeamUsage';
7
+ export { LiteLLMHomeWidget } from './components/LiteLLMHomeWidget';
8
+ export type { LiteLLMHomeWidgetProps } from './components/LiteLLMHomeWidget';
6
9
  export { LiteLlmApi, liteLlmApiRef } from './api';
7
10
  export type { LiteLlmApiInterface } from './api';
8
11
  export * from './types';
package/dist/index.esm.js CHANGED
@@ -218,8 +218,7 @@ var init_KeysTable = __esm({
218
218
  setFormData((f) => ({ ...f, team_id: teams[0].team_id }));
219
219
  }
220
220
  }, [generateModalOpen, teams, formData.team_id]);
221
- const teamRequired = teams.length > 0;
222
- const canGenerate = !teamRequired || !!formData.team_id;
221
+ const canGenerate = true;
223
222
  const [editingKey, setEditingKey] = useState(null);
224
223
  const [editForm, setEditForm] = useState({});
225
224
  const [editSubmitting, setEditSubmitting] = useState(false);
@@ -232,6 +231,10 @@ var init_KeysTable = __esm({
232
231
  const response = await onGenerateKey(formData);
233
232
  setNewKeyValue(response.key);
234
233
  setFormData(emptyForm());
234
+ setTimeout(() => {
235
+ setGenerateModalOpen(false);
236
+ setNewKeyValue(null);
237
+ }, 1500);
235
238
  } catch (error) {
236
239
  console.error("Failed to generate key:", error);
237
240
  } finally {
@@ -275,7 +278,25 @@ var init_KeysTable = __esm({
275
278
  onClick: () => setGenerateModalOpen(true)
276
279
  },
277
280
  "Generate New Key"
278
- )), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Spend"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM Limit"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, loading ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }))) : keys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys found"))) : keys.map((key) => /* @__PURE__ */ React2.createElement(TableRow, { key: key.key }, /* @__PURE__ */ React2.createElement(TableCell, null, key.key_alias || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", component: "code", sx: { fontFamily: "monospace" } }, showKeyValue === key.key ? key.key : maskKey(key.key)), /* @__PURE__ */ React2.createElement(
281
+ )), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Spend"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM Limit"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, loading ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }))) : keys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys found"))) : keys.map((key) => /* @__PURE__ */ React2.createElement(TableRow, { key: key.key }, /* @__PURE__ */ React2.createElement(TableCell, null, key.key_alias || "-"), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(
282
+ Typography2,
283
+ {
284
+ variant: "body2",
285
+ component: "code",
286
+ color: "primary",
287
+ sx: {
288
+ fontFamily: "monospace",
289
+ backgroundColor: "background.default",
290
+ px: 1,
291
+ py: 0.5,
292
+ borderRadius: 1,
293
+ maxWidth: "250px",
294
+ overflow: "hidden",
295
+ textOverflow: "ellipsis"
296
+ }
297
+ },
298
+ showKeyValue === key.key ? key.key : maskKey(key.key)
299
+ ), /* @__PURE__ */ React2.createElement(
279
300
  IconButton,
280
301
  {
281
302
  size: "small",
@@ -339,9 +360,7 @@ var init_KeysTable = __esm({
339
360
  {
340
361
  ...params,
341
362
  label: "Team",
342
- required: true,
343
- error: teamRequired && !formData.team_id,
344
- helperText: teamRequired && !formData.team_id ? "Pick a team to scope this key" : void 0,
363
+ helperText: "Optional: bind this key to a specific team for scoped access",
345
364
  fullWidth: true
346
365
  }
347
366
  )
@@ -385,7 +404,7 @@ var init_KeysTable = __esm({
385
404
  disabled: submitting || !canGenerate
386
405
  },
387
406
  submitting ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }) : "Generate"
388
- ))), /* @__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(
407
+ ), newKeyValue && /* @__PURE__ */ React2.createElement(Button, { onClick: handleCloseModal, variant: "contained", color: "success" }, "Done"))), /* @__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", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ React2.createElement(
389
408
  TextField,
390
409
  {
391
410
  label: "Alias",
@@ -486,11 +505,18 @@ var init_UsageStats = __esm({
486
505
  onDateRangeChange,
487
506
  loading
488
507
  }) => {
489
- const [selectedPreset, setSelectedPreset] = useState2("7d");
490
508
  const [selectedModel, setSelectedModel] = useState2("all");
491
509
  const [tab, setTab] = useState2("costs");
510
+ const selectedPreset = useMemo(() => {
511
+ const start = dateRange.start;
512
+ const end = dateRange.end;
513
+ if (start.toDateString() === end.toDateString()) return "today";
514
+ const diffMs = end.getTime() - start.getTime();
515
+ const diffDays = Math.ceil(diffMs / (1e3 * 60 * 60 * 24));
516
+ if (diffDays <= 7) return "7d";
517
+ return "30d";
518
+ }, [dateRange]);
492
519
  const handlePresetChange = (preset) => {
493
- setSelectedPreset(preset);
494
520
  const end = /* @__PURE__ */ new Date();
495
521
  const start = /* @__PURE__ */ new Date();
496
522
  if (preset === "today") start.setHours(0, 0, 0, 0);
@@ -811,10 +837,26 @@ var init_LiteLLMPage = __esm({
811
837
  async (keyId) => {
812
838
  try {
813
839
  await api.deleteKey(keyId);
814
- setSnackbar({ message: "Key revoked successfully", severity: "success" });
840
+ setSnackbar({
841
+ message: "Key revoked successfully",
842
+ severity: "success"
843
+ });
815
844
  refreshKeys();
816
845
  } catch (e) {
817
- setSnackbar({ message: `Failed to revoke key: ${e.message}`, severity: "error" });
846
+ if (e.body?.success && (e.body?.message?.includes("already deleted") || e.body?.message?.includes("never existed"))) {
847
+ setSnackbar({
848
+ message: "Key was already deleted",
849
+ severity: "warning"
850
+ });
851
+ refreshKeys();
852
+ return;
853
+ }
854
+ setSnackbar({
855
+ message: `Failed to revoke key: ${e.message}`,
856
+ severity: "error"
857
+ });
858
+ } finally {
859
+ refreshKeys();
818
860
  }
819
861
  },
820
862
  [api, refreshKeys]
@@ -874,42 +916,37 @@ var init_LiteLLMPage = __esm({
874
916
  }
875
917
  });
876
918
 
877
- // src/plugin.ts
919
+ // src/plugin.tsx
878
920
  init_api();
879
921
  import React6 from "react";
880
922
  import { TrendingUp as TrendingUpIcon } from "@mui/icons-material";
881
923
  import {
882
924
  createFrontendPlugin,
883
- createApiExtension,
884
- createPageExtension,
885
- createApiFactory,
886
- createSidebarExtension,
925
+ ApiBlueprint,
926
+ PageBlueprint,
887
927
  fetchApiRef
888
928
  } from "@backstage/frontend-plugin-api";
929
+ var liteLlmApi = ApiBlueprint.make({
930
+ params: (defineParams) => defineParams({
931
+ api: liteLlmApiRef,
932
+ deps: { fetchApi: fetchApiRef },
933
+ factory: ({ fetchApi }) => new LiteLlmApi(fetchApi)
934
+ })
935
+ });
936
+ var liteLlmPage = PageBlueprint.make({
937
+ params: {
938
+ path: "/litellm",
939
+ title: "LiteLLM",
940
+ icon: /* @__PURE__ */ React6.createElement(TrendingUpIcon, null),
941
+ loader: async () => {
942
+ const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
943
+ return /* @__PURE__ */ React6.createElement(LiteLLMPage2, null);
944
+ }
945
+ }
946
+ });
889
947
  var litellmPlugin = createFrontendPlugin({
890
- id: "litellm",
891
- extensions: [
892
- createApiExtension({
893
- factory: createApiFactory({
894
- api: liteLlmApiRef,
895
- deps: { fetchApi: fetchApiRef },
896
- factory: ({ fetchApi }) => new LiteLlmApi(fetchApi)
897
- })
898
- }),
899
- createSidebarExtension({
900
- id: "root",
901
- title: "LiteLLM",
902
- icon: /* @__PURE__ */ React6.createElement(TrendingUpIcon, null),
903
- defaultPath: "/litellm"
904
- }),
905
- createPageExtension({
906
- defaultPath: "/litellm",
907
- loader: async () => {
908
- const { LiteLLMPage: LiteLLMPage2 } = await Promise.resolve().then(() => (init_LiteLLMPage(), LiteLLMPage_exports));
909
- return React6.createElement(LiteLLMPage2);
910
- }
911
- })
912
- ]
948
+ pluginId: "litellm",
949
+ extensions: [liteLlmApi, liteLlmPage]
913
950
  });
914
951
 
915
952
  // src/index.ts
@@ -918,10 +955,106 @@ init_DashboardHeader();
918
955
  init_KeysTable();
919
956
  init_UsageStats();
920
957
  init_TeamUsage();
958
+
959
+ // src/components/LiteLLMHomeWidget.tsx
960
+ init_api();
961
+ import React7, { useState as useState5, useEffect as useEffect2 } from "react";
962
+ import {
963
+ Paper as Paper6,
964
+ Box as Box6,
965
+ Typography as Typography6,
966
+ FormControl as FormControl2,
967
+ Select as Select2,
968
+ MenuItem as MenuItem3,
969
+ Grid as Grid3,
970
+ CircularProgress as CircularProgress5,
971
+ Alert as Alert2
972
+ } from "@mui/material";
973
+ import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
974
+ import { useApi as useApi2 } from "@backstage/core-plugin-api";
975
+ var fmtUsd2 = (n) => `$${(n ?? 0).toFixed(n < 1 ? 4 : 2)}`;
976
+ var fmtInt2 = (n) => (n ?? 0).toLocaleString();
977
+ function presetToDateRange(preset) {
978
+ const end = /* @__PURE__ */ new Date();
979
+ const start = /* @__PURE__ */ new Date();
980
+ if (preset === "today") {
981
+ start.setHours(0, 0, 0, 0);
982
+ } else if (preset === "7d") {
983
+ start.setDate(start.getDate() - 7);
984
+ } else {
985
+ start.setDate(start.getDate() - 30);
986
+ }
987
+ return { start, end };
988
+ }
989
+ var Kpi = ({ label, value }) => /* @__PURE__ */ React7.createElement(Box6, null, /* @__PURE__ */ React7.createElement(Typography6, { variant: "caption", color: "text.secondary", display: "block" }, label), /* @__PURE__ */ React7.createElement(Typography6, { variant: "subtitle1", fontWeight: 600 }, value));
990
+ var LiteLLMHomeWidget = ({
991
+ defaultPeriod = "7d",
992
+ title = "LiteLLM Usage"
993
+ }) => {
994
+ const api = useApi2(liteLlmApiRef);
995
+ const [period, setPeriod] = useState5(defaultPeriod);
996
+ const [loading, setLoading] = useState5(true);
997
+ const [error, setError] = useState5(null);
998
+ const [usage, setUsage] = useState5(null);
999
+ const [keys, setKeys] = useState5([]);
1000
+ useEffect2(() => {
1001
+ let cancelled = false;
1002
+ setLoading(true);
1003
+ setError(null);
1004
+ const { start, end } = presetToDateRange(period);
1005
+ const startDate = start.toISOString().split("T")[0];
1006
+ const endDate = end.toISOString().split("T")[0];
1007
+ Promise.all([api.getUsage(startDate, endDate), api.listKeys()]).then(([usageData, keysData]) => {
1008
+ if (!cancelled) {
1009
+ setUsage(usageData);
1010
+ setKeys(keysData);
1011
+ setLoading(false);
1012
+ }
1013
+ }).catch((err) => {
1014
+ if (!cancelled) {
1015
+ setError(err.message ?? "Failed to load usage data");
1016
+ setLoading(false);
1017
+ }
1018
+ });
1019
+ return () => {
1020
+ cancelled = true;
1021
+ };
1022
+ }, [api, period]);
1023
+ const dailyData = (usage?.daily_usage ?? []).map((d) => ({
1024
+ date: d.date,
1025
+ spend: d.spend
1026
+ }));
1027
+ const hasSparkline = dailyData.length > 0;
1028
+ return /* @__PURE__ */ React7.createElement(Paper6, { sx: { p: 2 } }, /* @__PURE__ */ React7.createElement(Box6, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 1.5 }, /* @__PURE__ */ React7.createElement(Typography6, { variant: "h6" }, title), /* @__PURE__ */ React7.createElement(FormControl2, { size: "small", sx: { minWidth: 90 } }, /* @__PURE__ */ React7.createElement(
1029
+ Select2,
1030
+ {
1031
+ value: period,
1032
+ onChange: (e) => setPeriod(e.target.value),
1033
+ displayEmpty: true
1034
+ },
1035
+ /* @__PURE__ */ React7.createElement(MenuItem3, { value: "today" }, "Today"),
1036
+ /* @__PURE__ */ React7.createElement(MenuItem3, { value: "7d" }, "7d"),
1037
+ /* @__PURE__ */ React7.createElement(MenuItem3, { value: "30d" }, "30d")
1038
+ ))), loading && /* @__PURE__ */ React7.createElement(Box6, { display: "flex", justifyContent: "center", alignItems: "center", minHeight: 120 }, /* @__PURE__ */ React7.createElement(CircularProgress5, { size: 32 })), !loading && error && /* @__PURE__ */ React7.createElement(Alert2, { severity: "error", sx: { mt: 1 } }, error), !loading && !error && /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Grid3, { container: true, spacing: 2, sx: { mb: hasSparkline ? 1.5 : 0 } }, /* @__PURE__ */ React7.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React7.createElement(Kpi, { label: "USD Spent", value: fmtUsd2(usage?.total_spend ?? 0) })), /* @__PURE__ */ React7.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React7.createElement(Kpi, { label: "Tokens In", value: fmtInt2(usage?.prompt_tokens ?? 0) })), /* @__PURE__ */ React7.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React7.createElement(Kpi, { label: "Tokens Out", value: fmtInt2(usage?.completion_tokens ?? 0) })), /* @__PURE__ */ React7.createElement(Grid3, { item: true, xs: 6 }, /* @__PURE__ */ React7.createElement(Kpi, { label: "Keys", value: fmtInt2(keys.length) }))), hasSparkline && /* @__PURE__ */ React7.createElement(Box6, { height: 120 }, /* @__PURE__ */ React7.createElement(ResponsiveContainer3, { width: "100%", height: "100%" }, /* @__PURE__ */ React7.createElement(AreaChart3, { data: dailyData, margin: { top: 4, right: 0, bottom: 0, left: 0 } }, /* @__PURE__ */ React7.createElement(
1039
+ Area3,
1040
+ {
1041
+ type: "monotone",
1042
+ dataKey: "spend",
1043
+ stroke: "#8884d8",
1044
+ fill: "#8884d8",
1045
+ fillOpacity: 0.3,
1046
+ dot: false,
1047
+ isAnimationActive: false
1048
+ }
1049
+ ))))));
1050
+ };
1051
+
1052
+ // src/index.ts
921
1053
  init_api();
922
1054
  export {
923
1055
  DashboardHeader,
924
1056
  KeysTable,
1057
+ LiteLLMHomeWidget,
925
1058
  LiteLLMPage,
926
1059
  LiteLlmApi,
927
1060
  TeamUsage,