Compare commits
4 Commits
e00999b88c
...
5b5d7a95f3
| Author | SHA1 | Date |
|---|---|---|
|
|
5b5d7a95f3 | |
|
|
d455641b86 | |
|
|
e29f61aa68 | |
|
|
281c0de751 |
|
|
@ -23,6 +23,8 @@ import {
|
||||||
TrendingUp,
|
TrendingUp,
|
||||||
Gauge,
|
Gauge,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
|
Activity,
|
||||||
|
BarChart3,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
const COLORS = {
|
const COLORS = {
|
||||||
|
|
@ -34,10 +36,53 @@ const COLORS = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const CATEGORIES = [
|
const CATEGORIES = [
|
||||||
{ key: 'all', label: '全部', icon: Sparkles },
|
{ key: 'all', label: '全部', icon: BarChart3 },
|
||||||
{ key: 'language', label: '语言模型', icon: Brain },
|
{ key: 'language', label: '语言模型', icon: Brain },
|
||||||
{ key: 'image', label: '图像模型', icon: Zap },
|
{ key: 'image', label: '图像模型', icon: Sparkles },
|
||||||
{ key: 'video', label: '视频模型', icon: Zap },
|
{ key: 'video', label: '视频模型', icon: Activity },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* ─── Default benchmark data (fallback when API unavailable) ─── */
|
||||||
|
const DEFAULT_MODELS = [
|
||||||
|
// ── OpenAI ──
|
||||||
|
{ name: 'GPT-4o', provider: 'OpenAI', intelligence: 55, speed: 150, price: 2.50, latency: 0.8, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'GPT-4o Mini', provider: 'OpenAI', intelligence: 42, speed: 420, price: 0.15, latency: 0.4, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'o1', provider: 'OpenAI', intelligence: 58, speed: 30, price: 15.00, latency: 8.0, context_window: '200K', type: 'reasoning', category: 'language' },
|
||||||
|
{ name: 'o1-mini', provider: 'OpenAI', intelligence: 48, speed: 75, price: 3.00, latency: 3.0, context_window: '128K', type: 'reasoning', category: 'language' },
|
||||||
|
{ name: 'o3-mini', provider: 'OpenAI', intelligence: 50, speed: 85, price: 4.40, latency: 2.5, context_window: '200K', type: 'reasoning', category: 'language' },
|
||||||
|
{ name: 'GPT-4o Realtime', provider: 'OpenAI', intelligence: 54, speed: 55, price: 5.00, latency: 0.2, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
// ── Anthropic ──
|
||||||
|
{ name: 'Claude Sonnet 4', provider: 'Anthropic', intelligence: 56, speed: 120, price: 3.00, latency: 1.0, context_window: '200K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Claude Haiku 3.5', provider: 'Anthropic', intelligence: 44, speed: 380, price: 0.80, latency: 0.5, context_window: '200K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Claude Opus 4', provider: 'Anthropic', intelligence: 59, speed: 40, price: 15.00, latency: 4.0, context_window: '200K', type: 'general', category: 'language' },
|
||||||
|
// ── Google ──
|
||||||
|
{ name: 'Gemini 2.5 Pro', provider: 'Google', intelligence: 57, speed: 200, price: 1.25, latency: 1.2, context_window: '1M', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Gemini 2.0 Flash', provider: 'Google', intelligence: 46, speed: 550, price: 0.10, latency: 0.3, context_window: '1M', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Gemini 2.5 Flash', provider: 'Google', intelligence: 48, speed: 480, price: 0.15, latency: 0.4, context_window: '1M', type: 'general', category: 'language' },
|
||||||
|
// ── DeepSeek ──
|
||||||
|
{ name: 'DeepSeek-V3', provider: 'DeepSeek', intelligence: 52, speed: 220, price: 0.27, latency: 0.6, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'DeepSeek-R1', provider: 'DeepSeek', intelligence: 56, speed: 55, price: 0.55, latency: 8.0, context_window: '128K', type: 'reasoning', category: 'language' },
|
||||||
|
// ── Meta ──
|
||||||
|
{ name: 'Llama 3.1 405B', provider: 'Meta', intelligence: 50, speed: 60, price: 2.80, latency: 3.0, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Llama 3.1 70B', provider: 'Meta', intelligence: 44, speed: 180, price: 0.59, latency: 1.0, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
// ── Mistral ──
|
||||||
|
{ name: 'Mistral Large 2', provider: 'Mistral', intelligence: 51, speed: 140, price: 2.00, latency: 1.5, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Mistral Small', provider: 'Mistral', intelligence: 40, speed: 350, price: 0.20, latency: 0.4, context_window: '32K', type: 'general', category: 'language' },
|
||||||
|
// ── 国内模型 ──
|
||||||
|
{ name: 'Qwen-Max-0428', provider: 'Alibaba Cloud', intelligence: 52, speed: 160, price: 2.00, latency: 1.2, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Qwen-Plus', provider: 'Alibaba Cloud', intelligence: 44, speed: 280, price: 0.80, latency: 0.6, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Doubao-Pro-32k', provider: 'ByteDance', intelligence: 48, speed: 250, price: 0.60, latency: 0.8, context_window: '32K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'GLM-4-Plus', provider: 'Zhipu AI', intelligence: 47, speed: 200, price: 1.00, latency: 0.9, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
{ name: 'MiniMax-Text-01', provider: 'MiniMax', intelligence: 45, speed: 210, price: 0.80, latency: 0.7, context_window: '1M', type: 'general', category: 'language' },
|
||||||
|
{ name: 'Kimi-k2', provider: 'Moonshot', intelligence: 53, speed: 120, price: 2.00, latency: 1.8, context_window: '128K', type: 'general', category: 'language' },
|
||||||
|
// ── 图像模型 ──
|
||||||
|
{ name: 'DALL·E 3', provider: 'OpenAI', intelligence: 48, speed: 15, price: 40.00, latency: 5.0, context_window: '-', type: 'general', category: 'image' },
|
||||||
|
{ name: 'Stable Diffusion 3', provider: 'Stability AI', intelligence: 44, speed: 8, price: 6.50, latency: 8.0, context_window: '-', type: 'general', category: 'image' },
|
||||||
|
{ name: 'Midjourney 6', provider: 'Midjourney', intelligence: 50, speed: 12, price: 30.00, latency: 10.0, context_window: '-', type: 'general', category: 'image' },
|
||||||
|
{ name: 'Flux.1 Pro', provider: 'Black Forest Labs', intelligence: 47, speed: 10, price: 5.00, latency: 6.0, context_window: '-', type: 'general', category: 'image' },
|
||||||
|
// ── 视频模型 ──
|
||||||
|
{ name: 'Sora', provider: 'OpenAI', intelligence: 46, speed: 0.5, price: 100.00, latency: 30.0, context_window: '-', type: 'general', category: 'video' },
|
||||||
|
{ name: 'Runway Gen-3', provider: 'Runway', intelligence: 43, speed: 0.8, price: 50.00, latency: 25.0, context_window: '-', type: 'general', category: 'video' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const Benchmarks = () => {
|
const Benchmarks = () => {
|
||||||
|
|
@ -50,24 +95,26 @@ const Benchmarks = () => {
|
||||||
const [activeCategory, setActiveCategory] = useState('all');
|
const [activeCategory, setActiveCategory] = useState('all');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
// Show default data immediately for instant rendering
|
||||||
|
setModels(DEFAULT_MODELS);
|
||||||
|
// Try to fetch backend data in background
|
||||||
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await API.get('/api/option/');
|
const res = await API.get('/api/option/', { skipErrorHandler: true });
|
||||||
if (res.data.success && res.data.data) {
|
if (res.data?.success && res.data?.data) {
|
||||||
const option = res.data.data.find(
|
const option = res.data.data.find((o) => o.key === 'benchmark_data.models');
|
||||||
(o) => o.key === 'benchmark_data.models',
|
if (option?.value) {
|
||||||
);
|
const parsed = JSON.parse(option.value);
|
||||||
if (option && option.value) {
|
if (Array.isArray(parsed) && parsed.length > 0) {
|
||||||
setModels(JSON.parse(option.value));
|
setModels(parsed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// fallback: use embeded default data
|
// keep default data
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
};
|
})();
|
||||||
fetchData();
|
setLoading(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const filteredModels = useMemo(() => {
|
const filteredModels = useMemo(() => {
|
||||||
|
|
@ -120,19 +167,19 @@ const Benchmarks = () => {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-950 flex items-center justify-center">
|
<div className="min-h-screen bg-background flex items-center justify-center pt-14">
|
||||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-white/30" />
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-foreground/20" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const SortHeader = ({ label, sortField, icon: Icon }) => (
|
const SortHeader = ({ label, sortField, icon: Icon }) => (
|
||||||
<th
|
<th
|
||||||
className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider cursor-pointer hover:text-white/70 transition-colors select-none"
|
className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider cursor-pointer hover:text-foreground/70 transition-colors select-none"
|
||||||
onClick={() => toggleSort(sortField)}
|
onClick={() => toggleSort(sortField)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
{Icon && <Icon size={13} className="text-gray-500" />}
|
{Icon && <Icon size={13} className="text-muted-foreground/60" />}
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
{sortKey === sortField && (
|
{sortKey === sortField && (
|
||||||
<ArrowUpDown
|
<ArrowUpDown
|
||||||
|
|
@ -145,14 +192,14 @@ const Benchmarks = () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-950 text-white">
|
<div className="min-h-screen bg-background text-foreground pt-14">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="mb-8 sm:mb-12">
|
<div className="mb-8 sm:mb-12">
|
||||||
<h1 className="text-3xl sm:text-4xl font-bold font-heading mb-3">
|
<h1 className="text-3xl sm:text-4xl font-bold font-heading mb-3">
|
||||||
{t('AI 模型评测排行榜')}
|
{t('AI 模型评测排行榜')}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-400 text-sm sm:text-base font-body max-w-2xl">
|
<p className="text-muted-foreground text-sm sm:text-base font-body max-w-2xl">
|
||||||
{t('基于 Artificial Analysis 数据,从智能指数、输出速度、价格成本、延迟等维度对主流 AI 模型进行多维度对比评测')}
|
{t('基于 Artificial Analysis 数据,从智能指数、输出速度、价格成本、延迟等维度对主流 AI 模型进行多维度对比评测')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -160,82 +207,82 @@ const Benchmarks = () => {
|
||||||
{/* Highlight Cards */}
|
{/* Highlight Cards */}
|
||||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-8 sm:mb-10">
|
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-8 sm:mb-10">
|
||||||
{topIntel[0] && (
|
{topIntel[0] && (
|
||||||
<div className="bg-gradient-to-br from-purple-900/30 to-purple-950/30 border border-purple-800/30 rounded-xl p-4 sm:p-5">
|
<div className="bg-gradient-to-br from-purple-50 to-purple-100/50 dark:from-purple-950/40 dark:to-purple-900/20 border border-purple-200 dark:border-purple-800/30 rounded-xl p-4 sm:p-5">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Trophy size={18} className="text-purple-400" />
|
<Trophy size={18} className="text-purple-500 dark:text-purple-400" />
|
||||||
<span className="text-xs font-semibold text-purple-300 uppercase tracking-wider">
|
<span className="text-xs font-semibold text-purple-600 dark:text-purple-300 uppercase tracking-wider">
|
||||||
{t('智能冠军')}
|
{t('智能冠军')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl font-bold text-white mb-1">
|
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
|
||||||
{topIntel[0].name}
|
{topIntel[0].name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400 mb-2">{topIntel[0].provider}</div>
|
<div className="text-xs text-muted-foreground mb-2">{topIntel[0].provider}</div>
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<span className="text-2xl sm:text-3xl font-bold text-purple-400">
|
<span className="text-2xl sm:text-3xl font-bold text-purple-500 dark:text-purple-400">
|
||||||
{topIntel[0].intelligence}
|
{topIntel[0].intelligence}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">/ 60</span>
|
<span className="text-xs text-muted-foreground/60">/ 60</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{fastest && (
|
{fastest && (
|
||||||
<div className="bg-gradient-to-br from-cyan-900/30 to-cyan-950/30 border border-cyan-800/30 rounded-xl p-4 sm:p-5">
|
<div className="bg-gradient-to-br from-cyan-50 to-cyan-100/50 dark:from-cyan-950/40 dark:to-cyan-900/20 border border-cyan-200 dark:border-cyan-800/30 rounded-xl p-4 sm:p-5">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Gauge size={18} className="text-cyan-400" />
|
<Gauge size={18} className="text-cyan-500 dark:text-cyan-400" />
|
||||||
<span className="text-xs font-semibold text-cyan-300 uppercase tracking-wider">
|
<span className="text-xs font-semibold text-cyan-600 dark:text-cyan-300 uppercase tracking-wider">
|
||||||
{t('速度之王')}
|
{t('速度之王')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl font-bold text-white mb-1">
|
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
|
||||||
{fastest.name}
|
{fastest.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400 mb-2">{fastest.provider}</div>
|
<div className="text-xs text-muted-foreground mb-2">{fastest.provider}</div>
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<span className="text-2xl sm:text-3xl font-bold text-cyan-400">
|
<span className="text-2xl sm:text-3xl font-bold text-cyan-500 dark:text-cyan-400">
|
||||||
{fastest.speed?.toLocaleString()}
|
{fastest.speed?.toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">tok/s</span>
|
<span className="text-xs text-muted-foreground/60">tok/s</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{cheapest && (
|
{cheapest && (
|
||||||
<div className="bg-gradient-to-br from-emerald-900/30 to-emerald-950/30 border border-emerald-800/30 rounded-xl p-4 sm:p-5">
|
<div className="bg-gradient-to-br from-emerald-50 to-emerald-100/50 dark:from-emerald-950/40 dark:to-emerald-900/20 border border-emerald-200 dark:border-emerald-800/30 rounded-xl p-4 sm:p-5">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<DollarSign size={18} className="text-emerald-400" />
|
<DollarSign size={18} className="text-emerald-500 dark:text-emerald-400" />
|
||||||
<span className="text-xs font-semibold text-emerald-300 uppercase tracking-wider">
|
<span className="text-xs font-semibold text-emerald-600 dark:text-emerald-300 uppercase tracking-wider">
|
||||||
{t('性价比首选')}
|
{t('性价比首选')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl font-bold text-white mb-1">
|
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
|
||||||
{cheapest.name}
|
{cheapest.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400 mb-2">{cheapest.provider}</div>
|
<div className="text-xs text-muted-foreground mb-2">{cheapest.provider}</div>
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<span className="text-2xl sm:text-3xl font-bold text-emerald-400">
|
<span className="text-2xl sm:text-3xl font-bold text-emerald-500 dark:text-emerald-400">
|
||||||
${cheapest.price?.toFixed(2)}
|
${cheapest.price?.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">/M tokens</span>
|
<span className="text-xs text-muted-foreground/60">/M tokens</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{lowestLatency && (
|
{lowestLatency && (
|
||||||
<div className="bg-gradient-to-br from-amber-900/30 to-amber-950/30 border border-amber-800/30 rounded-xl p-4 sm:p-5">
|
<div className="bg-gradient-to-br from-amber-50 to-amber-100/50 dark:from-amber-950/40 dark:to-amber-900/20 border border-amber-200 dark:border-amber-800/30 rounded-xl p-4 sm:p-5">
|
||||||
<div className="flex items-center gap-2 mb-3">
|
<div className="flex items-center gap-2 mb-3">
|
||||||
<Clock size={18} className="text-amber-400" />
|
<Clock size={18} className="text-amber-500 dark:text-amber-400" />
|
||||||
<span className="text-xs font-semibold text-amber-300 uppercase tracking-wider">
|
<span className="text-xs font-semibold text-amber-600 dark:text-amber-300 uppercase tracking-wider">
|
||||||
{t('低延迟标杆')}
|
{t('低延迟标杆')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-lg sm:text-xl font-bold text-white mb-1">
|
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
|
||||||
{lowestLatency.name}
|
{lowestLatency.name}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400 mb-2">{lowestLatency.provider}</div>
|
<div className="text-xs text-muted-foreground mb-2">{lowestLatency.provider}</div>
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<span className="text-2xl sm:text-3xl font-bold text-amber-400">
|
<span className="text-2xl sm:text-3xl font-bold text-amber-500 dark:text-amber-400">
|
||||||
{lowestLatency.latency?.toFixed(2)}
|
{lowestLatency.latency?.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">{t('秒')}</span>
|
<span className="text-xs text-muted-foreground/60">{t('秒')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -253,8 +300,8 @@ const Benchmarks = () => {
|
||||||
onClick={() => setActiveCategory(cat.key)}
|
onClick={() => setActiveCategory(cat.key)}
|
||||||
className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 ${
|
className={`flex items-center gap-1.5 px-3.5 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-white/10 text-white border border-white/20'
|
? 'bg-primary/10 text-foreground border border-border'
|
||||||
: 'bg-white/5 text-gray-400 border border-transparent hover:bg-white/10 hover:text-gray-300'
|
: 'bg-muted/40 text-muted-foreground border border-transparent hover:bg-muted/60 hover:text-foreground/80'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Icon size={13} />
|
<Icon size={13} />
|
||||||
|
|
@ -266,34 +313,34 @@ const Benchmarks = () => {
|
||||||
<div className="relative w-full sm:w-64">
|
<div className="relative w-full sm:w-64">
|
||||||
<Search
|
<Search
|
||||||
size={15}
|
size={15}
|
||||||
className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
|
className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground/60"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={t('搜索模型名称或供应商...')}
|
placeholder={t('搜索模型名称或供应商...')}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
className="w-full bg-white/5 border border-white/10 rounded-lg pl-9 pr-3 py-2 text-sm text-white placeholder-gray-500 focus:outline-none focus:border-white/30 focus:bg-white/10 transition-all"
|
className="w-full bg-muted/30 border border-input rounded-lg pl-9 pr-3 py-2 text-sm text-foreground placeholder-muted-foreground/50 focus:outline-none focus:border-ring focus:bg-muted/50 transition-all"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Model Count */}
|
{/* Model Count */}
|
||||||
<div className="text-xs text-gray-500 mb-3">
|
<div className="text-xs text-muted-foreground/60 mb-3">
|
||||||
{t('共 {count} 个模型', { count: filteredModels.length })}
|
{t('共 {count} 个模型', { count: filteredModels.length })}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Table */}
|
{/* Table */}
|
||||||
<div className="overflow-x-auto rounded-xl border border-white/10">
|
<div className="overflow-x-auto rounded-xl border border-border">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="bg-white/5 border-b border-white/10">
|
<tr className="bg-muted/30 border-b border-border">
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider w-10">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider w-10">
|
||||||
#
|
#
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<Brain size={13} className="text-gray-500" />
|
<Brain size={13} className="text-muted-foreground/60" />
|
||||||
<span>{t('模型名称')}</span>
|
<span>{t('模型名称')}</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
|
|
@ -309,39 +356,39 @@ const Benchmarks = () => {
|
||||||
icon={DollarSign}
|
icon={DollarSign}
|
||||||
/>
|
/>
|
||||||
<SortHeader label="延迟" sortField="latency" icon={Clock} />
|
<SortHeader label="延迟" sortField="latency" icon={Clock} />
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<Maximize2 size={13} className="text-gray-500" />
|
<Maximize2 size={13} className="text-muted-foreground/60" />
|
||||||
<span>{t('上下文')}</span>
|
<span>{t('上下文')}</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-400 uppercase tracking-wider">
|
<th className="px-4 py-3 text-center text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
||||||
{t('类型')}
|
{t('类型')}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="divide-y divide-white/5">
|
<tbody className="divide-y divide-border/40">
|
||||||
{filteredModels.map((model, idx) => (
|
{filteredModels.map((model, idx) => (
|
||||||
<tr
|
<tr
|
||||||
key={model.name + model.provider}
|
key={model.name + model.provider}
|
||||||
className="hover:bg-white/5 transition-colors"
|
className="hover:bg-muted/50 transition-colors"
|
||||||
>
|
>
|
||||||
<td className="px-4 py-3.5 text-gray-500 text-xs font-mono">
|
<td className="px-4 py-3.5 text-muted-foreground text-xs font-mono">
|
||||||
{idx + 1}
|
{idx + 1}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="font-medium text-white text-sm">
|
<span className="font-medium text-foreground text-sm">
|
||||||
{model.name}
|
{model.name}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-gray-500">
|
<span className="text-xs text-muted-foreground">
|
||||||
{model.provider}
|
{model.provider}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-20 sm:w-24 h-2 bg-white/10 rounded-full overflow-hidden">
|
<div className="w-20 sm:w-24 h-2 bg-muted rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-full transition-all"
|
className="h-full rounded-full transition-all"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -350,14 +397,14 @@ const Benchmarks = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-mono text-gray-300 w-8 text-right">
|
<span className="text-xs font-mono text-foreground/70 w-8 text-right">
|
||||||
{model.intelligence}
|
{model.intelligence}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<div className="w-16 sm:w-20 h-2 bg-white/10 rounded-full overflow-hidden">
|
<div className="w-16 sm:w-20 h-2 bg-muted rounded-full overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-full transition-all"
|
className="h-full rounded-full transition-all"
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -366,23 +413,23 @@ const Benchmarks = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-mono text-gray-300 whitespace-nowrap">
|
<span className="text-xs font-mono text-foreground/70 whitespace-nowrap">
|
||||||
{model.speed?.toLocaleString()}
|
{model.speed?.toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<span className="text-xs font-mono text-gray-300">
|
<span className="text-xs font-mono text-foreground/70">
|
||||||
${model.price?.toFixed(2)}
|
${model.price?.toFixed(2)}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<span className="text-xs font-mono text-gray-300">
|
<span className="text-xs font-mono text-foreground/70">
|
||||||
{model.latency?.toFixed(2)}s
|
{model.latency?.toFixed(2)}s
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-3.5">
|
<td className="px-4 py-3.5">
|
||||||
<span className="text-xs text-gray-400">
|
<span className="text-xs text-muted-foreground">
|
||||||
{model.context_window || '-'}
|
{model.context_window || '-'}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -390,8 +437,8 @@ const Benchmarks = () => {
|
||||||
<span
|
<span
|
||||||
className={`inline-block px-2 py-0.5 rounded text-[10px] font-medium ${
|
className={`inline-block px-2 py-0.5 rounded text-[10px] font-medium ${
|
||||||
model.type === 'reasoning'
|
model.type === 'reasoning'
|
||||||
? 'bg-purple-900/50 text-purple-300 border border-purple-700/50'
|
? 'bg-purple-100 text-purple-700 border border-purple-200 dark:bg-purple-900/50 dark:text-purple-300 dark:border-purple-700/50'
|
||||||
: 'bg-blue-900/50 text-blue-300 border border-blue-700/50'
|
: 'bg-blue-100 text-blue-700 border border-blue-200 dark:bg-blue-900/50 dark:text-blue-300 dark:border-blue-700/50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{model.type === 'reasoning'
|
{model.type === 'reasoning'
|
||||||
|
|
@ -404,14 +451,14 @@ const Benchmarks = () => {
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{filteredModels.length === 0 && (
|
{filteredModels.length === 0 && (
|
||||||
<div className="text-center py-12 text-gray-500 text-sm">
|
<div className="text-center py-12 text-muted-foreground text-sm">
|
||||||
{t('暂未找到匹配的模型')}
|
{t('暂未找到匹配的模型')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer */}
|
{/* Footer */}
|
||||||
<div className="mt-6 text-center text-xs text-gray-600">
|
<div className="mt-6 text-center text-xs text-muted-foreground/50">
|
||||||
{t('数据来源: Artificial Analysis | 数据持续更新中')}
|
{t('数据来源: Artificial Analysis | 数据持续更新中')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue