144 lines
4.8 KiB
JavaScript
144 lines
4.8 KiB
JavaScript
/*
|
|
Copyright (C) 2025 QuantumNous
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
For commercial licensing, please contact support@quantumnous.com
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { Activity, CreditCard, Hash, Layers, Gauge } from 'lucide-react';
|
|
import { renderQuota } from '../../helpers';
|
|
|
|
const formatNumber = (n) => {
|
|
if (n == null || isNaN(n)) return '—';
|
|
const v = Number(n);
|
|
if (v >= 1_000_000) return (v / 1_000_000).toFixed(1) + 'M';
|
|
if (v >= 1_000) return (v / 1_000).toFixed(1) + 'K';
|
|
return v.toLocaleString();
|
|
};
|
|
|
|
const formatLatency = (v) => {
|
|
if (v == null) return '—';
|
|
if (v < 1) return `${Math.round(v * 1000)}ms`;
|
|
return `${v.toFixed(2)}s`;
|
|
};
|
|
|
|
const BentoStat = ({ label, value, unit, sub, delta, icon: Icon, className = '' }) => {
|
|
return (
|
|
<div
|
|
className={`relative flex flex-col gap-3 rounded-2xl border border-slate-200 bg-white p-5 transition-shadow hover:shadow-md dark:border-white/5 dark:bg-white/[0.02] dark:hover:shadow-black/20 ${className}`}
|
|
>
|
|
<div className='flex items-center gap-3'>
|
|
<div className='flex h-9 w-9 items-center justify-center rounded-lg bg-slate-100 text-slate-600 dark:bg-white/5 dark:text-white/70'>
|
|
<Icon size={16} />
|
|
</div>
|
|
<div className='text-sm font-semibold text-slate-900 dark:text-white'>{label}</div>
|
|
</div>
|
|
|
|
<div className='flex flex-1 flex-col justify-center'>
|
|
<div className='flex items-baseline gap-2'>
|
|
<span className='font-mono text-3xl font-bold tabular-nums leading-none text-slate-900 md:text-4xl dark:text-white'>
|
|
{value}
|
|
</span>
|
|
{unit && <span className='text-xs text-slate-400 dark:text-white/40'>{unit}</span>}
|
|
</div>
|
|
</div>
|
|
|
|
<div className='space-y-1.5'>
|
|
{delta != null && (
|
|
<div
|
|
className={`inline-flex items-center gap-1.5 text-xs tabular-nums ${
|
|
delta < 0
|
|
? 'text-emerald-600 dark:text-emerald-400'
|
|
: delta > 0
|
|
? 'text-red-500 dark:text-red-400'
|
|
: 'text-slate-500 dark:text-white/50'
|
|
}`}
|
|
>
|
|
<span className='font-semibold'>
|
|
{delta > 0 ? '↑' : delta < 0 ? '↓' : '·'} {Math.abs(delta).toFixed(0)}%
|
|
</span>
|
|
<span className='font-normal text-slate-400 dark:text-white/40'>对比上月</span>
|
|
</div>
|
|
)}
|
|
{sub && (
|
|
<div className='text-[11px] text-slate-500 tabular-nums dark:text-white/40'>{sub}</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const BentoStats = ({ metrics, monthDelta }) => {
|
|
return (
|
|
<>
|
|
<BentoStat
|
|
label='今日请求'
|
|
value={metrics.today.requests}
|
|
unit='次'
|
|
sub={
|
|
<span>
|
|
<span className='font-semibold text-slate-900 dark:text-white'>
|
|
{formatNumber(metrics.today.tokens)}
|
|
</span>{' '}
|
|
tokens
|
|
<span className='mx-1.5 text-slate-300 dark:text-white/30'>·</span>
|
|
{renderQuota(metrics.today.cost)}
|
|
</span>
|
|
}
|
|
icon={Activity}
|
|
/>
|
|
<BentoStat
|
|
label='今日 token'
|
|
value={formatNumber(metrics.today.tokens)}
|
|
unit='tokens'
|
|
sub={`今日花费 ${renderQuota(metrics.today.cost)}`}
|
|
icon={Hash}
|
|
/>
|
|
<BentoStat
|
|
label='平均响应'
|
|
value={formatLatency(metrics.today.avgLatency)}
|
|
sub='今日成功调用'
|
|
icon={Gauge}
|
|
/>
|
|
<BentoStat
|
|
label='本月消费'
|
|
value={metrics.month.cost ? renderQuota(metrics.month.cost) : '—'}
|
|
sub={
|
|
<span>
|
|
<span className='font-semibold tabular-nums text-slate-900 dark:text-white'>
|
|
{metrics.month.requests}
|
|
</span>{' '}
|
|
次调用
|
|
<span className='mx-1.5 text-slate-300 dark:text-white/30'>·</span>
|
|
<span className='tabular-nums'>{formatNumber(metrics.month.tokens)}</span> tokens
|
|
</span>
|
|
}
|
|
delta={monthDelta}
|
|
icon={CreditCard}
|
|
/>
|
|
<BentoStat
|
|
label='累计 token'
|
|
value={formatNumber(metrics.month.tokens)}
|
|
unit='本月'
|
|
sub='输入+输出'
|
|
icon={Layers}
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default BentoStats;
|