Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| import { type ReactNode, useEffect, useMemo, useState } from 'react'; | |
| import { | |
| Alert, | |
| Box, | |
| Button, | |
| CircularProgress, | |
| Divider, | |
| Link, | |
| Popover, | |
| Tooltip, | |
| Typography, | |
| } from '@mui/material'; | |
| import PaidOutlinedIcon from '@mui/icons-material/PaidOutlined'; | |
| import OpenInNewIcon from '@mui/icons-material/OpenInNew'; | |
| import { useSessionStore } from '@/store/sessionStore'; | |
| import { | |
| type HfAccountUsageBucket, | |
| type HfInferenceProvidersCredits, | |
| type UsageBucket, | |
| useUsageStore, | |
| } from '@/store/usageStore'; | |
| function formatUsd(value: number | undefined): string { | |
| const amount = value ?? 0; | |
| if (amount > 0 && amount < 0.01) return '<$0.01'; | |
| return new Intl.NumberFormat('en-US', { | |
| style: 'currency', | |
| currency: 'USD', | |
| minimumFractionDigits: 2, | |
| maximumFractionDigits: 2, | |
| }).format(amount); | |
| } | |
| function formatCount(value: number | undefined): string { | |
| return new Intl.NumberFormat('en-US').format(value ?? 0); | |
| } | |
| function contextTokenCount(telemetry: UsageBucket | null | undefined): number | undefined { | |
| if (!telemetry) return undefined; | |
| if (telemetry.total_tokens > 0) { | |
| return Math.max(0, telemetry.total_tokens - telemetry.completion_tokens); | |
| } | |
| return ( | |
| telemetry.prompt_tokens + | |
| telemetry.cache_read_tokens + | |
| telemetry.cache_creation_tokens | |
| ); | |
| } | |
| function billingUnavailableMessage(error: string | null | undefined): string | null { | |
| if (!error) return null; | |
| if (error === 'missing_hf_token') return 'Sign in to view HF account billing usage.'; | |
| if (error === 'billing_usage_unavailable') { | |
| return 'HF billing usage is unavailable. Showing app-observed calls and tokens; inference cost may be incomplete.'; | |
| } | |
| return 'HF billing usage is unavailable. Showing app-observed calls and tokens; inference cost may be incomplete.'; | |
| } | |
| function UsageRow({ | |
| label, | |
| value, | |
| strong = false, | |
| }: { | |
| label: string; | |
| value: string; | |
| strong?: boolean; | |
| }) { | |
| return ( | |
| <> | |
| <Typography variant="body2" color="text.secondary"> | |
| {label} | |
| </Typography> | |
| <Typography | |
| variant="body2" | |
| sx={{ fontWeight: strong ? 700 : 400, fontVariantNumeric: 'tabular-nums' }} | |
| > | |
| {value} | |
| </Typography> | |
| </> | |
| ); | |
| } | |
| function UsageGrid({ children }: { children: ReactNode }) { | |
| return ( | |
| <Box | |
| sx={{ | |
| display: 'grid', | |
| gridTemplateColumns: '1fr auto', | |
| columnGap: 2, | |
| rowGap: 0.5, | |
| mt: 0.75, | |
| }} | |
| > | |
| {children} | |
| </Box> | |
| ); | |
| } | |
| function AccountUsageSection({ | |
| title, | |
| account, | |
| telemetry, | |
| }: { | |
| title: string; | |
| account: HfAccountUsageBucket | null | undefined; | |
| telemetry: UsageBucket | null | undefined; | |
| }) { | |
| const useJobEstimate = | |
| !account || (account.hf_jobs_usd <= 0 && (telemetry?.hf_jobs_estimated_usd ?? 0) > 0); | |
| return ( | |
| <Box sx={{ py: 1 }}> | |
| <Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 700 }}> | |
| {title} | |
| </Typography> | |
| <UsageGrid> | |
| <UsageRow | |
| label="Inference Providers" | |
| value={account ? formatUsd(account.inference_providers_usd) : 'Unavailable'} | |
| strong | |
| /> | |
| <UsageRow | |
| label="HF Jobs" | |
| value={formatUsd( | |
| useJobEstimate ? telemetry?.hf_jobs_estimated_usd : account?.hf_jobs_usd, | |
| )} | |
| /> | |
| <UsageRow | |
| label="HF Sandboxes" | |
| value={formatUsd(telemetry?.sandbox_estimated_usd)} | |
| /> | |
| <UsageRow label="LLM calls" value={formatCount(telemetry?.llm_calls)} /> | |
| <UsageRow | |
| label="Input tokens" | |
| value={formatCount(contextTokenCount(telemetry))} | |
| /> | |
| <UsageRow | |
| label="Output tokens" | |
| value={formatCount(telemetry?.completion_tokens)} | |
| /> | |
| </UsageGrid> | |
| </Box> | |
| ); | |
| } | |
| function CreditsSection({ credits }: { credits: HfInferenceProvidersCredits | null | undefined }) { | |
| if (!credits) return null; | |
| return ( | |
| <> | |
| <Divider /> | |
| <Box sx={{ py: 1 }}> | |
| <Typography variant="caption" sx={{ color: 'text.secondary', fontWeight: 700 }}> | |
| Inference credits | |
| </Typography> | |
| <UsageGrid> | |
| <UsageRow | |
| label="Included remaining" | |
| value={formatUsd(credits.remaining_included_usd)} | |
| strong | |
| /> | |
| <UsageRow | |
| label="Used / included" | |
| value={`${formatUsd(credits.used_usd)} / ${formatUsd(credits.included_usd)}`} | |
| /> | |
| {credits.limit_usd > 0 && ( | |
| <UsageRow | |
| label="Spend limit remaining" | |
| value={formatUsd(credits.remaining_limit_usd)} | |
| /> | |
| )} | |
| </UsageGrid> | |
| </Box> | |
| </> | |
| ); | |
| } | |
| export default function UsageMeter() { | |
| const activeSessionId = useSessionStore((state) => state.activeSessionId); | |
| const activeSessionYoloSpend = useSessionStore((state) => { | |
| const active = state.sessions.find((session) => session.id === state.activeSessionId); | |
| return active?.autoApprovalEstimatedSpendUsd ?? null; | |
| }); | |
| const { usage, isLoading, error, fetchUsage } = useUsageStore(); | |
| const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null); | |
| useEffect(() => { | |
| void fetchUsage(activeSessionId); | |
| }, [activeSessionId, activeSessionYoloSpend, fetchUsage]); | |
| const accountSessionInference = | |
| usage?.hf_account?.current_session?.inference_providers_usd; | |
| const sessionTotal = | |
| accountSessionInference == null | |
| ? usage?.session?.total_usd | |
| : accountSessionInference + | |
| (usage?.session?.hf_jobs_estimated_usd ?? 0) + | |
| (usage?.session?.sandbox_estimated_usd ?? 0); | |
| const links = useMemo(() => usage?.links ?? {}, [usage?.links]); | |
| const billingMessage = billingUnavailableMessage(usage?.hf_account?.error); | |
| const open = Boolean(anchorEl); | |
| return ( | |
| <> | |
| <Tooltip title="Usage"> | |
| <Button | |
| size="small" | |
| variant="outlined" | |
| startIcon={isLoading ? <CircularProgress size={14} /> : <PaidOutlinedIcon fontSize="small" />} | |
| onClick={(event) => setAnchorEl(event.currentTarget)} | |
| sx={{ | |
| minWidth: { xs: 58, sm: 84 }, | |
| height: 32, | |
| px: { xs: 0.75, sm: 1 }, | |
| borderColor: 'divider', | |
| color: 'text.secondary', | |
| fontVariantNumeric: 'tabular-nums', | |
| '& .MuiButton-startIcon': { mr: { xs: 0.25, sm: 0.5 } }, | |
| '&:hover': { borderColor: 'primary.main', color: 'primary.main' }, | |
| }} | |
| > | |
| {sessionTotal == null ? 'Usage' : formatUsd(sessionTotal)} | |
| </Button> | |
| </Tooltip> | |
| <Popover | |
| open={open} | |
| anchorEl={anchorEl} | |
| onClose={() => setAnchorEl(null)} | |
| anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} | |
| transformOrigin={{ vertical: 'top', horizontal: 'right' }} | |
| slotProps={{ | |
| paper: { | |
| sx: { | |
| width: 320, | |
| maxWidth: 'calc(100vw - 24px)', | |
| maxHeight: 'calc(100vh - 24px)', | |
| overflowY: 'auto', | |
| p: 2, | |
| border: '1px solid', | |
| borderColor: 'divider', | |
| }, | |
| }, | |
| }} | |
| > | |
| <Typography variant="subtitle2" sx={{ fontWeight: 800 }}> | |
| Usage | |
| </Typography> | |
| <Typography variant="caption" color="text.secondary"> | |
| Estimated from HF account usage per session. | |
| </Typography> | |
| {error ? ( | |
| <Typography variant="body2" color="error" sx={{ mt: 1.5 }}> | |
| {error} | |
| </Typography> | |
| ) : ( | |
| <> | |
| {billingMessage && ( | |
| <Alert severity="info" sx={{ mt: 1.5, py: 0.25 }}> | |
| {billingMessage} | |
| </Alert> | |
| )} | |
| <AccountUsageSection | |
| title="Current session" | |
| account={usage?.hf_account?.current_session ?? null} | |
| telemetry={usage?.session ?? null} | |
| /> | |
| <CreditsSection credits={usage?.hf_account?.inference_providers_credits} /> | |
| </> | |
| )} | |
| <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, pt: 1 }}> | |
| {links.hf_billing && ( | |
| <Link href={links.hf_billing} target="_blank" rel="noopener noreferrer" underline="hover" sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.25, fontSize: '0.75rem' }}> | |
| HF billing <OpenInNewIcon sx={{ fontSize: 12 }} /> | |
| </Link> | |
| )} | |
| {links.jobs_pricing && ( | |
| <Link href={links.jobs_pricing} target="_blank" rel="noopener noreferrer" underline="hover" sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.25, fontSize: '0.75rem' }}> | |
| Jobs pricing <OpenInNewIcon sx={{ fontSize: 12 }} /> | |
| </Link> | |
| )} | |
| </Box> | |
| </Popover> | |
| </> | |
| ); | |
| } | |