fix(ui): api usage

This commit is contained in:
null 2026-05-20 23:46:45 -05:00
parent aa62d285cd
commit 9650283367
1 changed files with 107 additions and 97 deletions

View File

@ -282,6 +282,41 @@ function fmtLatencyMs(ms: number | null | undefined): string {
return `${(ms / 1000).toFixed(2)}s`;
}
function usageBarColor(pct: number): string {
if (pct > 90) return "bg-[color:var(--danger)]";
if (pct > 75) return "bg-[color:var(--warning)]";
return "bg-[color:var(--success)]";
}
function fmtPct(pct: number): string {
return `${Math.max(0, Math.min(100, pct)).toFixed(0)}%`;
}
interface UsageWindowBarProps {
label: string;
pct: number;
resetInMs?: number | null;
}
function UsageWindowBar({ label, pct, resetInMs }: UsageWindowBarProps) {
const clamped = Math.max(0, Math.min(100, pct));
return (
<div className="space-y-1">
<div className="flex items-center justify-between text-[11px]">
<span className="font-medium text-muted">{label}</span>
<span className="tabular-nums text-strong">{fmtPct(clamped)} used</span>
</div>
<div className="h-1.5 overflow-hidden rounded-full bg-[color:var(--surface-strong)]">
<div
className={`h-full rounded-full transition-all ${usageBarColor(clamped)}`}
style={{ width: `${clamped}%` }}
/>
</div>
<p className="text-[11px] text-muted">Resets in {fmtResetMs(resetInMs)}</p>
</div>
);
}
function UsageStrip({ credentialId, provider }: { credentialId: string; provider: string }) {
const [usage, setUsage] = useState<ProviderUsageLiveRead | null>(null);
const [loading, setLoading] = useState(true);
@ -333,6 +368,28 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
const inputTok = usage.input_tokens;
const req = usage.requests;
const isOllama = provider === "ollama";
const usageBars: UsageWindowBarProps[] = [];
if (inputTok.pct_used != null) {
usageBars.push({
label: "Current session",
pct: inputTok.pct_used,
resetInMs: inputTok.reset_in_ms,
});
}
if (tok.pct_used != null) {
usageBars.push({
label: usageBars.length > 0 ? "All models" : "Usage",
pct: tok.pct_used,
resetInMs: tok.reset_in_ms,
});
}
if (usageBars.length === 0 && req.limit != null && req.remaining != null && req.limit > 0) {
usageBars.push({
label: "Requests",
pct: ((req.limit - req.remaining) / req.limit) * 100,
resetInMs: req.reset_in_ms,
});
}
return (
<div className="mt-2 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] p-2.5">
@ -350,29 +407,34 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
<RefreshCw className={`h-3 w-3 ${loading ? "animate-spin" : ""}`} />
</button>
</div>
{(usage.sample_input_tokens != null || usage.sample_output_tokens != null) && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Usage (last probe)</span>
<span className="tabular-nums text-strong">
in {fmtTokens(usage.sample_input_tokens)} · out {fmtTokens(usage.sample_output_tokens)}
</span>
</div>
)}
{usage.sample_latency_ms != null && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Time (last probe)</span>
<span className="tabular-nums text-strong">
{fmtLatencyMs(usage.sample_latency_ms)}
</span>
</div>
)}
{usage.sample_latency_ms != null && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Time (last probe)</span>
<span className="tabular-nums text-strong">
{fmtLatencyMs(usage.sample_latency_ms)}
</span>
{usageBars.length > 0 ? (
<div className="space-y-2">
{usageBars.map((bar) => (
<UsageWindowBar key={bar.label} label={bar.label} pct={bar.pct} resetInMs={bar.resetInMs} />
))}
</div>
) : (
<>
{(usage.sample_input_tokens != null || usage.sample_output_tokens != null) && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Usage (last probe)</span>
<span className="tabular-nums text-strong">
in {fmtTokens(usage.sample_input_tokens)} · out {fmtTokens(usage.sample_output_tokens)}
</span>
</div>
)}
{usage.sample_latency_ms != null && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Time (last probe)</span>
<span className="tabular-nums text-strong">
{fmtLatencyMs(usage.sample_latency_ms)}
</span>
</div>
)}
<p className="text-[11px] text-muted">
This provider did not return active usage windows for percent + reset tracking.
</p>
</>
)}
<div className="flex items-center justify-between text-[11px] text-muted">
{lastFetched && <span>Updated {Math.round((Date.now() - lastFetched.getTime()) / 1000)}s ago</span>}
@ -380,86 +442,34 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div>
) : (
<div className="space-y-1.5">
{(usage.sample_input_tokens != null || usage.sample_output_tokens != null) && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Usage (last probe)</span>
<span className="tabular-nums text-strong">
in {fmtTokens(usage.sample_input_tokens)} · out {fmtTokens(usage.sample_output_tokens)}
</span>
{usageBars.length > 0 ? (
<div className="space-y-2">
{usageBars.map((bar) => (
<UsageWindowBar key={bar.label} label={bar.label} pct={bar.pct} resetInMs={bar.resetInMs} />
))}
</div>
)}
{/* Tokens */}
{tok.limit != null ? (
<div>
<div className="mb-1 flex items-center justify-between text-[11px]">
<span className="font-medium text-muted">Tokens</span>
<span className="tabular-nums text-strong">
{fmtTokens(tok.remaining)} / {fmtTokens(tok.limit)} remaining
{tok.reset_in_ms != null && (
<span className="ml-2 text-muted">· resets in {fmtResetMs(tok.reset_in_ms)}</span>
)}
</span>
</div>
{tok.pct_used != null && (
<div className="h-1.5 overflow-hidden rounded-full bg-[color:var(--surface-strong)]">
<div
className={`h-full rounded-full transition-all ${
tok.pct_used > 90 ? "bg-[color:var(--danger)]" :
tok.pct_used > 75 ? "bg-[color:var(--warning)]" :
"bg-[color:var(--success)]"
}`}
style={{ width: `${Math.min(100, tok.pct_used)}%` }}
/>
) : (
<>
{(usage.sample_input_tokens != null || usage.sample_output_tokens != null) && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Usage (last probe)</span>
<span className="tabular-nums text-strong">
in {fmtTokens(usage.sample_input_tokens)} · out {fmtTokens(usage.sample_output_tokens)}
</span>
</div>
)}
</div>
) : null}
{/* Anthropic input tokens */}
{inputTok.limit != null ? (
<div>
<div className="mb-1 flex items-center justify-between text-[11px]">
<span className="font-medium text-muted">Input tokens</span>
<span className="tabular-nums text-strong">
{fmtTokens(inputTok.remaining)} / {fmtTokens(inputTok.limit)} remaining
{inputTok.reset_in_ms != null && (
<span className="ml-2 text-muted">· resets in {fmtResetMs(inputTok.reset_in_ms)}</span>
)}
</span>
</div>
{inputTok.pct_used != null && (
<div className="h-1.5 overflow-hidden rounded-full bg-[color:var(--surface-strong)]">
<div
className={`h-full rounded-full transition-all ${
inputTok.pct_used > 90 ? "bg-[color:var(--danger)]" :
inputTok.pct_used > 75 ? "bg-[color:var(--warning)]" :
"bg-[color:var(--success)]"
}`}
style={{ width: `${Math.min(100, inputTok.pct_used)}%` }}
/>
{usage.sample_latency_ms != null && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Time (last probe)</span>
<span className="tabular-nums text-strong">
{fmtLatencyMs(usage.sample_latency_ms)}
</span>
</div>
)}
</div>
) : null}
{/* Requests */}
{req.limit != null ? (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Requests</span>
<span className="tabular-nums">
{req.remaining ?? "—"} / {req.limit} remaining
{req.reset_in_ms != null && (
<span className="ml-2">· resets in {fmtResetMs(req.reset_in_ms)}</span>
)}
</span>
</div>
) : null}
{tok.limit == null && inputTok.limit == null && req.limit == null && (
<p className="text-[11px] text-muted">
Connected no token/request limit windows were returned for this key right now.
</p>
<p className="text-[11px] text-muted">
Connected provider did not return usage windows for percent + reset tracking.
</p>
</>
)}
<div className="flex items-center justify-between text-[11px] text-muted">