Files
shishirpatil--gorilla/agent-arena/client/src/components/Leaderboard.js
T
wehub-resource-sync bbfc60cd69
Publish BFCL to PyPI / build_and_publish (push) Waiting to run
Update API Zoo Data / send-updates (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:37:27 +08:00

459 lines
16 KiB
JavaScript

import React, { useState, useEffect, useContext } from 'react';
import axios from 'axios';
import { Container, Card } from 'react-bootstrap';
import Select from 'react-select';
import { Analytics } from "@vercel/analytics/react";
import { ThemeContext } from "../App";
// Filter colors for tools and agents based on their categories
const toolFilterColors = {
'Search Engines': { normal: '#c5cae9', hover: '#9fa8da' },
'Simple Math': { normal: '#ffd3b6', hover: '#ffbfa0' },
'Knowledge Bases': { normal: '#ffe0b2', hover: '#ffcc80' },
'Math/CS Academic Search': { normal: '#ffccbc', hover: '#ffab91' },
'Code Interpreter': { normal: '#a9cce3', hover: '#87bdd8' },
};
const agentFilterColors = {
'Search Engines': { normal: '#c5cae9', hover: '#9fa8da' },
'Simple Math': { normal: '#ffd3b6', hover: '#ffbfa0' },
'Knowledge Bases': { normal: '#ffe0b2', hover: '#ffcc80' },
'Math/CS Academic Search': { normal: '#ffccbc', hover: '#ffab91' },
'Code Interpreter': { normal: '#a9cce3', hover: '#87bdd8' },
};
// Tools (and agents) to include in the leaderboard
const validToolNames = [
'google-serper', 'you-search', 'tavily-search', 'exa-search', 'brave-search',
'llamaindex-code-interpreter', 'openai-code-interpreter', 'riza-code-interpreter', 'python-repl',
'wolfram-alpha', 'calculator', 'wikipedia', 'asknews', 'arxiv'
];
// Framework-specific colors with hover states
const frameworkColors = {
langchain: { normal: '#a3e4d7', hover: '#76d7c4' },
llamaindex: { normal: '#d1f2eb', hover: '#b2e4d5' },
composio: { normal: '#f5cba7', hover: '#f4b184' },
crewai: { normal: '#f9e79f', hover: '#f7dc6f' },
'openai assistants': { normal: '#a9cce3', hover: '#87bdd8' },
'anthropic tool use': { normal: '#f7cac9', hover: '#f4b0a9' }
};
// Adjusted model-specific colors
const modelColors = {
OpenAI: { normal: '#9bc2c4', hover: '#76c776' },
Anthropic: { normal: '#ffbfa0', hover: '#ff7f50' },
Perplexity: { normal: '#A9A9A9', hover: '#808080' },
'Mistral AI': { normal: '#FFA500', hover: '#FF8C00' },
Google: { normal: '#4285F4', hover: '#357ae8' },
Meta: { normal: '#4267B2', hover: '#365899' }
};
// Provider options for filtering models by provider with colors in the dropdown
const providerOptions = Object.keys(modelColors).map(provider => ({
value: provider,
label: (
<span>
<span
style={{
display: 'inline-block',
width: '12px',
height: '12px',
borderRadius: '50%',
backgroundColor: modelColors[provider].normal,
marginRight: '8px'
}}
></span>
{provider}
</span>
)
}));
// Tool category options based on toolFilterColors
const toolCategoryOptions = Object.keys(toolFilterColors).map(cat => ({
value: cat,
label: (
<span>
<span
style={{
display: 'inline-block',
width: '12px',
height: '12px',
borderRadius: '50%',
backgroundColor: toolFilterColors[cat].normal,
marginRight: '8px'
}}
></span>
{cat}
</span>
)
}));
const metricOptions = [
{ value: 'skillParameter', label: 'Skill Parameter' },
{ value: 'pass', label: 'Pass Count' },
{ value: 'fail', label: 'Fail Count' },
{ value: 'executionTime', label: 'Execution Time' },
{ value: 'votes', label: 'Total Votes' },
{ value: 'name', label: 'Alphabetical' }
];
const customStyles = {
control: (base) => ({
...base,
minHeight: 40,
fontSize: 14
}),
menu: (base) => ({
...base,
fontSize: 14
}),
singleValue: (base) => ({
...base,
color: '#4b0082'
}),
option: (base, { isFocused }) => ({
...base,
color: '#4b0082',
backgroundColor: isFocused ? '#d3d3d3' : 'white'
})
};
const parseAgentData = (fileContent) => {
const categories = ['Search Engines', 'Simple Math', 'Knowledge Bases', 'Math/CS Academic Search', 'Code Interpreter'];
const parsedData = {};
const allAgentNames = new Set();
categories.forEach(category => {
parsedData[category] = {};
});
let currentCategory = '';
const lines = fileContent.split('\n');
lines.forEach(line => {
categories.forEach(category => {
if (line.includes(`ELO Ratings for ${category} category`)) {
currentCategory = category;
}
});
const match = line.match(/(.+):\s+(\d+\.\d+)/);
if (match && currentCategory) {
const agentName = match[1].trim();
const eloRating = parseFloat(match[2].trim());
parsedData[currentCategory][agentName] = eloRating;
allAgentNames.add(agentName);
}
});
return { parsedData, allAgentNames };
};
const LeaderboardTable = ({ title, data, sortBy, selectedCategory }) => {
const tableStyles = {
width: '100%',
borderCollapse: 'collapse',
backgroundColor: '#1e272e',
color: '#4b0082'
};
const thStyles = {
backgroundColor: '#34495e',
color: '#ffffff',
padding: '12px',
textAlign: 'center',
borderBottom: '2px solid #2c3e50'
};
const tdStyles = {
padding: '12px',
textAlign: 'center',
borderBottom: '1px solid #34495e'
};
const getAgentColor = (categories) => {
if (!Array.isArray(categories)) {
return agentFilterColors[categories] || { normal: '#2c3e50', hover: '#34495e' };
}
for (let category of categories) {
if (agentFilterColors[category]) {
return agentFilterColors[category];
}
}
return { normal: '#2c3e50', hover: '#34495e' };
};
return (
<Card className="p-3 mb-4 shadow-sm" style={{ backgroundColor: '#2c3e50', borderRadius: '8px' }}>
<h3 className="text-center" style={{ color: '#ffffff' }}>{title}</h3>
<div style={{ maxHeight: '400px', overflowY: data.length > 10 ? 'scroll' : 'auto' }}>
<table style={tableStyles}>
<thead>
<tr>
<th style={thStyles}>#</th>
<th style={{ ...thStyles, textAlign: 'left' }}>Name</th>
<th style={thStyles}>Skill Parameter</th>
<th style={thStyles}>Vote %</th>
{sortBy === 'agents' && <>
<th style={thStyles}>Average Time (s)</th>
<th style={thStyles}>Success Rate</th>
</>}
</tr>
</thead>
<tbody>
{data.map((item, index) => {
let backgroundColor = '#2c3e50';
let hoverColor = '#34495e';
if (sortBy === 'agents') {
const colors = getAgentColor(item.category);
backgroundColor = colors.normal;
hoverColor = colors.hover;
} else if (sortBy === 'tools') {
const categoryToUse = Array.isArray(item.category)
? (selectedCategory && item.category.includes(selectedCategory)
? selectedCategory
: item.category[0])
: item.category;
backgroundColor = toolFilterColors[categoryToUse]?.normal || backgroundColor;
hoverColor = toolFilterColors[categoryToUse]?.hover || hoverColor;
} else if (sortBy === 'models') {
backgroundColor = modelColors[item.provider]?.normal || backgroundColor;
hoverColor = modelColors[item.provider]?.hover || hoverColor;
} else if (sortBy === 'frameworks') {
backgroundColor = frameworkColors[item.name.toLowerCase()]?.normal || backgroundColor;
hoverColor = frameworkColors[item.name.toLowerCase()]?.hover || hoverColor;
}
const successRate = item.passCount + item.failCount > 0
? ((item.passCount / (item.passCount + item.failCount))).toFixed(2)
: 'N/A';
return (
<tr
key={index}
style={{ backgroundColor }}
onMouseEnter={(e) => e.currentTarget.style.backgroundColor = hoverColor}
onMouseLeave={(e) => e.currentTarget.style.backgroundColor = backgroundColor}
>
<td style={tdStyles}>{index + 1}</td>
<td style={{ ...tdStyles, textAlign: 'left' }}>{item.name}</td>
<td style={tdStyles}>{item.skillParameter.toFixed(2)}</td>
<td style={tdStyles}>{item.votePercentage}</td>
{sortBy === 'agents' && <>
<td style={tdStyles}>{item.averageExecutionTime.toFixed(2)}</td>
<td style={tdStyles}>{successRate}</td>
</>}
</tr>
);
})}
</tbody>
</table>
</div>
</Card>
);
};
// Main Leaderboard Component
const Leaderboard = () => {
const sortData = (data, sortField) => {
return [...data].sort((a, b) => {
if (sortField === 'name') {
return a.name.localeCompare(b.name);
} else {
return b[sortField] - a[sortField];
}
});
};
const [agents, setAgents] = useState([]);
const [models, setModels] = useState([]);
const [tools, setTools] = useState([]);
const [frameworks, setFrameworks] = useState([]);
const [category, setCategory] = useState('Search Engines'); // Default agent category
const [toolCategory, setToolCategory] = useState('Code Interpreter'); // Default tool category
const [provider, setProvider] = useState(''); // Provider filter
const [agentSortBy, setAgentSortBy] = useState('skillParameter'); // Sort by field for agents
const [toolSortBy] = useState('skill'); // Sort by field for tools
const { theme } = useContext(ThemeContext);
const [rawAgentData, setRawAgentData] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
const encodedToolCategory = encodeURIComponent(toolCategory);
const encodedCategory = encodeURIComponent(category);
const fileResponse = await fetch('/elo_ratings_by_category.txt');
const fileContent = await fileResponse.text();
const { parsedData, allAgentNames } = parseAgentData(fileContent);
const agentsResponse = await axios.get(`https://agent-arena.vercel.app/api/leaderboard?category=${encodedCategory}&sortBy=${agentSortBy}`);
const toolsResponse = await axios.get(`https://agent-arena.vercel.app/api/leaderboard/tools?category=${encodedToolCategory}&sortBy=${toolSortBy}`);
const modelsResponse = await axios.get(`https://agent-arena.vercel.app/api/leaderboard/models?sortBy=${toolSortBy}&provider=${provider}`);
const frameworksResponse = await axios.get(`https://agent-arena.vercel.app/api/leaderboard/frameworks`);
const filteredAgents = agentsResponse.data
.filter(agent => allAgentNames.has(agent.name))
.map(agent => ({
...agent,
skillParameter: parsedData[category][agent.name] || agent.skillParameter
}));
setRawAgentData(filteredAgents);
setAgents(sortData(filteredAgents, agentSortBy));
const toolsData = toolsResponse.data.filter(tool => validToolNames.includes(tool.name));
const sortedTools = sortData(toolsData, toolSortBy);
setTools(sortedTools);
setModels(modelsResponse.data);
setFrameworks(frameworksResponse.data);
} catch (error) {
console.error("Error fetching leaderboard data:", error);
}
};
fetchData();
}, [category, toolCategory, provider, agentSortBy]);
useEffect(() => {
setAgents(sortData(rawAgentData, agentSortBy));
}, [agentSortBy, rawAgentData]);
const handleCategoryChange = (selectedOption) => {
setCategory(selectedOption ? selectedOption.value : '');
};
const handleToolCategoryChange = (selectedOption) => {
setToolCategory(selectedOption ? selectedOption.value : '');
};
const handleProviderChange = (selectedOption) => {
setProvider(selectedOption ? selectedOption.value : '');
};
const handleSortByChange = (selectedOption) => {
setAgentSortBy(selectedOption ? selectedOption.value : 'skillParameter');
};
return (
<Container fluid>
<h1 className="text-center my-4" style={{ color: '#ffffff' }}>Leaderboard</h1>
<p className="text-center mb-4" style={{color: theme === "light" ? "#000" : "#ffffff"}}>
The Leaderboard ranks AI agents, models, tools, and frameworks using ELO-style ratings from battles.
It offers insights into agent capabilities across various categories and leverages battle data to evaluate individual agent components,
providing a comprehensive view of agent performance in different domains.
</p>
{/* Tools and Models Leaderboards Side by Side */}
<div className="leaderboard-container mb-4">
<div className="leaderboard-item">
<div className="mb-3">
<label htmlFor="provider-select" style={{ color: theme === "light" ? "#000" : "#ffffff", marginBottom: '5px', display: 'block' }}>Select Provider</label>
<Select
id="provider-select"
options={providerOptions}
onChange={handleProviderChange}
isClearable
placeholder="Sort by Provider"
styles={customStyles}
/>
</div>
<LeaderboardTable
title={`Models Leaderboard${provider ? ` (${provider})` : ''}`}
data={models}
sortBy="models"
/>
</div>
<div className="leaderboard-item">
<div className="mb-3">
<label htmlFor="tool-category-select" style={{ color: theme === "light" ? "#000" : "#ffffff", marginBottom: '5px', display: 'block' }}>Select Tool Category</label>
<Select
id="tool-category-select"
options={toolCategoryOptions}
onChange={handleToolCategoryChange}
isClearable
placeholder="Select Tool Category"
styles={customStyles}
defaultValue={toolCategoryOptions.find(option => option.value === 'Code Interpreter')}
/>
</div>
<LeaderboardTable
title={`Tools Leaderboard${toolCategory ? ` (${toolCategory})` : ''}`}
data={tools}
sortBy="tools"
/>
</div>
</div>
{/* Frameworks Leaderboard */}
<LeaderboardTable title="Frameworks Leaderboard" data={frameworks} sortBy="frameworks" />
{/* Dropdowns for Agents Leaderboard */}
<div className="d-flex justify-content-between mb-3 flex-column flex-md-row align-items-start align-items-md-stretch">
<div className="dropdown-container mb-3 mb-md-0">
<label htmlFor="category-select" style={{ color: theme === "light" ? "#000" : "#ffffff", marginBottom: '5px', display: 'block' }}>Select Agent Category</label>
<Select
id="category-select"
options={toolCategoryOptions}
onChange={handleCategoryChange}
isClearable
placeholder="Select Category"
styles={customStyles}
defaultValue={toolCategoryOptions.find(option => option.value === 'Search Engines')}
/>
</div>
<div className="dropdown-container">
<label htmlFor="metric-select" style={{ color: theme === "light" ? "#000" : "#ffffff", marginBottom: '5px', display: 'block' }}>Sort by Metric</label>
<Select
id="metric-select"
options={metricOptions}
onChange={handleSortByChange}
defaultValue={metricOptions[0]}
styles={customStyles}
/>
</div>
</div>
<LeaderboardTable
title={`Agents Leaderboard${category ? ` (${category})` : ''}`}
data={agents}
sortBy="agents"
selectedCategory={category}
/>
<style jsx>{`
.dropdown-container {
width: 100%;
}
@media (min-width: 768px) {
.dropdown-container {
width: 48%;
}
}
.leaderboard-container {
display: flex;
flex-direction: column;
}
@media (min-width: 768px) {
.leaderboard-container {
flex-direction: row;
justify-content: space-between;
}
.leaderboard-item {
width: 48%;
}
}
`}</style>
</Container>
);
};
export default Leaderboard;