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 (
<>
{label}
{value}
>
);
}
function UsageGrid({ children }: { children: ReactNode }) {
return (
{children}
);
}
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 (
{title}
);
}
function CreditsSection({ credits }: { credits: HfInferenceProvidersCredits | null | undefined }) {
if (!credits) return null;
return (
<>
Inference credits
{credits.limit_usd > 0 && (
)}
>
);
}
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(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 (
<>
: }
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)}
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',
},
},
}}
>
Usage
Estimated from HF account usage per session.
{error ? (
{error}
) : (
<>
{billingMessage && (
{billingMessage}
)}
>
)}
{links.hf_billing && (
HF billing
)}
{links.jobs_pricing && (
Jobs pricing
)}
>
);
}