Compare commits

..

No commits in common. "5b5d7a95f3a226047cf4bbe05de7768e7ee6d621" and "e00999b88cb70f3786ea4f2ddbc22c3bd86b6461" have entirely different histories.

2 changed files with 80 additions and 126 deletions

1
$null
View File

@ -0,0 +1 @@
ERROR: The process "main.exe" not found.

View File

@ -23,8 +23,6 @@ import {
TrendingUp,
Gauge,
Sparkles,
Activity,
BarChart3,
} from 'lucide-react';
const COLORS = {
@ -36,53 +34,10 @@ const COLORS = {
};
const CATEGORIES = [
{ key: 'all', label: '全部', icon: BarChart3 },
{ key: 'all', label: '全部', icon: Sparkles },
{ key: 'language', label: '语言模型', icon: Brain },
{ key: 'image', label: '图像模型', icon: Sparkles },
{ 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' },
{ key: 'image', label: '图像模型', icon: Zap },
{ key: 'video', label: '视频模型', icon: Zap },
];
const Benchmarks = () => {
@ -95,26 +50,24 @@ const Benchmarks = () => {
const [activeCategory, setActiveCategory] = useState('all');
useEffect(() => {
// Show default data immediately for instant rendering
setModels(DEFAULT_MODELS);
// Try to fetch backend data in background
(async () => {
const fetchData = async () => {
try {
const res = await API.get('/api/option/', { skipErrorHandler: true });
if (res.data?.success && res.data?.data) {
const option = res.data.data.find((o) => o.key === 'benchmark_data.models');
if (option?.value) {
const parsed = JSON.parse(option.value);
if (Array.isArray(parsed) && parsed.length > 0) {
setModels(parsed);
}
const res = await API.get('/api/option/');
if (res.data.success && res.data.data) {
const option = res.data.data.find(
(o) => o.key === 'benchmark_data.models',
);
if (option && option.value) {
setModels(JSON.parse(option.value));
}
}
} catch {
// keep default data
// fallback: use embeded default data
} finally {
setLoading(false);
}
})();
setLoading(false);
};
fetchData();
}, []);
const filteredModels = useMemo(() => {
@ -167,19 +120,19 @@ const Benchmarks = () => {
if (loading) {
return (
<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-foreground/20" />
<div className="min-h-screen bg-gray-950 flex items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-white/30" />
</div>
);
}
const SortHeader = ({ label, sortField, icon: Icon }) => (
<th
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"
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"
onClick={() => toggleSort(sortField)}
>
<div className="flex items-center gap-1.5">
{Icon && <Icon size={13} className="text-muted-foreground/60" />}
{Icon && <Icon size={13} className="text-gray-500" />}
<span>{label}</span>
{sortKey === sortField && (
<ArrowUpDown
@ -192,14 +145,14 @@ const Benchmarks = () => {
);
return (
<div className="min-h-screen bg-background text-foreground pt-14">
<div className="min-h-screen bg-gray-950 text-white">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 sm:py-12">
{/* Header */}
<div className="mb-8 sm:mb-12">
<h1 className="text-3xl sm:text-4xl font-bold font-heading mb-3">
{t('AI 模型评测排行榜')}
</h1>
<p className="text-muted-foreground text-sm sm:text-base font-body max-w-2xl">
<p className="text-gray-400 text-sm sm:text-base font-body max-w-2xl">
{t('基于 Artificial Analysis 数据,从智能指数、输出速度、价格成本、延迟等维度对主流 AI 模型进行多维度对比评测')}
</p>
</div>
@ -207,82 +160,82 @@ const Benchmarks = () => {
{/* Highlight Cards */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-8 sm:mb-10">
{topIntel[0] && (
<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="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="flex items-center gap-2 mb-3">
<Trophy size={18} className="text-purple-500 dark:text-purple-400" />
<span className="text-xs font-semibold text-purple-600 dark:text-purple-300 uppercase tracking-wider">
<Trophy size={18} className="text-purple-400" />
<span className="text-xs font-semibold text-purple-300 uppercase tracking-wider">
{t('智能冠军')}
</span>
</div>
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
<div className="text-lg sm:text-xl font-bold text-white mb-1">
{topIntel[0].name}
</div>
<div className="text-xs text-muted-foreground mb-2">{topIntel[0].provider}</div>
<div className="text-xs text-gray-400 mb-2">{topIntel[0].provider}</div>
<div className="flex items-baseline gap-1">
<span className="text-2xl sm:text-3xl font-bold text-purple-500 dark:text-purple-400">
<span className="text-2xl sm:text-3xl font-bold text-purple-400">
{topIntel[0].intelligence}
</span>
<span className="text-xs text-muted-foreground/60">/ 60</span>
<span className="text-xs text-gray-500">/ 60</span>
</div>
</div>
)}
{fastest && (
<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="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="flex items-center gap-2 mb-3">
<Gauge size={18} className="text-cyan-500 dark:text-cyan-400" />
<span className="text-xs font-semibold text-cyan-600 dark:text-cyan-300 uppercase tracking-wider">
<Gauge size={18} className="text-cyan-400" />
<span className="text-xs font-semibold text-cyan-300 uppercase tracking-wider">
{t('速度之王')}
</span>
</div>
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
<div className="text-lg sm:text-xl font-bold text-white mb-1">
{fastest.name}
</div>
<div className="text-xs text-muted-foreground mb-2">{fastest.provider}</div>
<div className="text-xs text-gray-400 mb-2">{fastest.provider}</div>
<div className="flex items-baseline gap-1">
<span className="text-2xl sm:text-3xl font-bold text-cyan-500 dark:text-cyan-400">
<span className="text-2xl sm:text-3xl font-bold text-cyan-400">
{fastest.speed?.toLocaleString()}
</span>
<span className="text-xs text-muted-foreground/60">tok/s</span>
<span className="text-xs text-gray-500">tok/s</span>
</div>
</div>
)}
{cheapest && (
<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="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="flex items-center gap-2 mb-3">
<DollarSign size={18} className="text-emerald-500 dark:text-emerald-400" />
<span className="text-xs font-semibold text-emerald-600 dark:text-emerald-300 uppercase tracking-wider">
<DollarSign size={18} className="text-emerald-400" />
<span className="text-xs font-semibold text-emerald-300 uppercase tracking-wider">
{t('性价比首选')}
</span>
</div>
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
<div className="text-lg sm:text-xl font-bold text-white mb-1">
{cheapest.name}
</div>
<div className="text-xs text-muted-foreground mb-2">{cheapest.provider}</div>
<div className="text-xs text-gray-400 mb-2">{cheapest.provider}</div>
<div className="flex items-baseline gap-1">
<span className="text-2xl sm:text-3xl font-bold text-emerald-500 dark:text-emerald-400">
<span className="text-2xl sm:text-3xl font-bold text-emerald-400">
${cheapest.price?.toFixed(2)}
</span>
<span className="text-xs text-muted-foreground/60">/M tokens</span>
<span className="text-xs text-gray-500">/M tokens</span>
</div>
</div>
)}
{lowestLatency && (
<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="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="flex items-center gap-2 mb-3">
<Clock size={18} className="text-amber-500 dark:text-amber-400" />
<span className="text-xs font-semibold text-amber-600 dark:text-amber-300 uppercase tracking-wider">
<Clock size={18} className="text-amber-400" />
<span className="text-xs font-semibold text-amber-300 uppercase tracking-wider">
{t('低延迟标杆')}
</span>
</div>
<div className="text-lg sm:text-xl font-bold text-foreground mb-1">
<div className="text-lg sm:text-xl font-bold text-white mb-1">
{lowestLatency.name}
</div>
<div className="text-xs text-muted-foreground mb-2">{lowestLatency.provider}</div>
<div className="text-xs text-gray-400 mb-2">{lowestLatency.provider}</div>
<div className="flex items-baseline gap-1">
<span className="text-2xl sm:text-3xl font-bold text-amber-500 dark:text-amber-400">
<span className="text-2xl sm:text-3xl font-bold text-amber-400">
{lowestLatency.latency?.toFixed(2)}
</span>
<span className="text-xs text-muted-foreground/60">{t('秒')}</span>
<span className="text-xs text-gray-500">{t('秒')}</span>
</div>
</div>
)}
@ -300,8 +253,8 @@ const Benchmarks = () => {
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 ${
isActive
? 'bg-primary/10 text-foreground border border-border'
: 'bg-muted/40 text-muted-foreground border border-transparent hover:bg-muted/60 hover:text-foreground/80'
? 'bg-white/10 text-white border border-white/20'
: 'bg-white/5 text-gray-400 border border-transparent hover:bg-white/10 hover:text-gray-300'
}`}
>
<Icon size={13} />
@ -313,34 +266,34 @@ const Benchmarks = () => {
<div className="relative w-full sm:w-64">
<Search
size={15}
className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground/60"
className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
/>
<input
type="text"
placeholder={t('搜索模型名称或供应商...')}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
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"
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"
/>
</div>
</div>
{/* Model Count */}
<div className="text-xs text-muted-foreground/60 mb-3">
<div className="text-xs text-gray-500 mb-3">
{t('共 {count} 个模型', { count: filteredModels.length })}
</div>
{/* Table */}
<div className="overflow-x-auto rounded-xl border border-border">
<div className="overflow-x-auto rounded-xl border border-white/10">
<table className="w-full text-sm">
<thead>
<tr className="bg-muted/30 border-b border-border">
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider w-10">
<tr className="bg-white/5 border-b border-white/10">
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider w-10">
#
</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider">
<div className="flex items-center gap-1.5">
<Brain size={13} className="text-muted-foreground/60" />
<Brain size={13} className="text-gray-500" />
<span>{t('模型名称')}</span>
</div>
</th>
@ -356,39 +309,39 @@ const Benchmarks = () => {
icon={DollarSign}
/>
<SortHeader label="延迟" sortField="latency" icon={Clock} />
<th className="px-4 py-3 text-left text-xs font-semibold text-muted-foreground uppercase tracking-wider">
<th className="px-4 py-3 text-left text-xs font-semibold text-gray-400 uppercase tracking-wider">
<div className="flex items-center gap-1.5">
<Maximize2 size={13} className="text-muted-foreground/60" />
<Maximize2 size={13} className="text-gray-500" />
<span>{t('上下文')}</span>
</div>
</th>
<th className="px-4 py-3 text-center text-xs font-semibold text-muted-foreground uppercase tracking-wider">
<th className="px-4 py-3 text-center text-xs font-semibold text-gray-400 uppercase tracking-wider">
{t('类型')}
</th>
</tr>
</thead>
<tbody className="divide-y divide-border/40">
<tbody className="divide-y divide-white/5">
{filteredModels.map((model, idx) => (
<tr
key={model.name + model.provider}
className="hover:bg-muted/50 transition-colors"
className="hover:bg-white/5 transition-colors"
>
<td className="px-4 py-3.5 text-muted-foreground text-xs font-mono">
<td className="px-4 py-3.5 text-gray-500 text-xs font-mono">
{idx + 1}
</td>
<td className="px-4 py-3.5">
<div className="flex flex-col">
<span className="font-medium text-foreground text-sm">
<span className="font-medium text-white text-sm">
{model.name}
</span>
<span className="text-xs text-muted-foreground">
<span className="text-xs text-gray-500">
{model.provider}
</span>
</div>
</td>
<td className="px-4 py-3.5">
<div className="flex items-center gap-2">
<div className="w-20 sm:w-24 h-2 bg-muted rounded-full overflow-hidden">
<div className="w-20 sm:w-24 h-2 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all"
style={{
@ -397,14 +350,14 @@ const Benchmarks = () => {
}}
/>
</div>
<span className="text-xs font-mono text-foreground/70 w-8 text-right">
<span className="text-xs font-mono text-gray-300 w-8 text-right">
{model.intelligence}
</span>
</div>
</td>
<td className="px-4 py-3.5">
<div className="flex items-center gap-2">
<div className="w-16 sm:w-20 h-2 bg-muted rounded-full overflow-hidden">
<div className="w-16 sm:w-20 h-2 bg-white/10 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all"
style={{
@ -413,23 +366,23 @@ const Benchmarks = () => {
}}
/>
</div>
<span className="text-xs font-mono text-foreground/70 whitespace-nowrap">
<span className="text-xs font-mono text-gray-300 whitespace-nowrap">
{model.speed?.toLocaleString()}
</span>
</div>
</td>
<td className="px-4 py-3.5">
<span className="text-xs font-mono text-foreground/70">
<span className="text-xs font-mono text-gray-300">
${model.price?.toFixed(2)}
</span>
</td>
<td className="px-4 py-3.5">
<span className="text-xs font-mono text-foreground/70">
<span className="text-xs font-mono text-gray-300">
{model.latency?.toFixed(2)}s
</span>
</td>
<td className="px-4 py-3.5">
<span className="text-xs text-muted-foreground">
<span className="text-xs text-gray-400">
{model.context_window || '-'}
</span>
</td>
@ -437,8 +390,8 @@ const Benchmarks = () => {
<span
className={`inline-block px-2 py-0.5 rounded text-[10px] font-medium ${
model.type === 'reasoning'
? '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-100 text-blue-700 border border-blue-200 dark:bg-blue-900/50 dark:text-blue-300 dark:border-blue-700/50'
? 'bg-purple-900/50 text-purple-300 border border-purple-700/50'
: 'bg-blue-900/50 text-blue-300 border border-blue-700/50'
}`}
>
{model.type === 'reasoning'
@ -451,14 +404,14 @@ const Benchmarks = () => {
</tbody>
</table>
{filteredModels.length === 0 && (
<div className="text-center py-12 text-muted-foreground text-sm">
<div className="text-center py-12 text-gray-500 text-sm">
{t('暂未找到匹配的模型')}
</div>
)}
</div>
{/* Footer */}
<div className="mt-6 text-center text-xs text-muted-foreground/50">
<div className="mt-6 text-center text-xs text-gray-600">
{t('数据来源: Artificial Analysis | 数据持续更新中')}
</div>
</div>