Spaces:
Running
Running
add fe
Browse files- .gitignore +41 -0
- Makefile +58 -0
- README.md +83 -6
- main.py +79 -0
- requirements/base.txt +7 -0
- requirements/dev.txt +5 -0
- requirements/prod.txt +1 -0
- src/__init__.py +0 -0
- src/app.py +15 -0
- src/components/__init__.py +0 -0
- src/components/filters.py +50 -0
- src/components/header.py +30 -0
- src/components/visualizations.py +119 -0
- src/core/__init__.py +0 -0
- src/core/config.py +16 -0
- src/core/styles.py +62 -0
- src/main.py +4 -0
- src/services/__init__.py +0 -0
- src/services/api.py +38 -0
- src/static/.DS_Store +0 -0
- src/static/images/favicon.png +0 -0
- src/static/images/hf-logo.png +0 -0
- src/static/images/pocketpal-ai-logo.png +0 -0
.gitignore
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
*.so
|
6 |
+
.Python
|
7 |
+
build/
|
8 |
+
develop-eggs/
|
9 |
+
dist/
|
10 |
+
downloads/
|
11 |
+
eggs/
|
12 |
+
.eggs/
|
13 |
+
lib/
|
14 |
+
lib64/
|
15 |
+
parts/
|
16 |
+
sdist/
|
17 |
+
var/
|
18 |
+
wheels/
|
19 |
+
*.egg-info/
|
20 |
+
.installed.cfg
|
21 |
+
*.egg
|
22 |
+
|
23 |
+
# Virtual Environment
|
24 |
+
venv/
|
25 |
+
ENV/
|
26 |
+
|
27 |
+
# Environment Variables
|
28 |
+
.env
|
29 |
+
.env.local
|
30 |
+
|
31 |
+
# IDE
|
32 |
+
.idea/
|
33 |
+
.vscode/
|
34 |
+
*.swp
|
35 |
+
*.swo
|
36 |
+
|
37 |
+
# Streamlit
|
38 |
+
.streamlit/
|
39 |
+
|
40 |
+
# Logs
|
41 |
+
*.log
|
Makefile
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.PHONY: install venv run test clean lint format help
|
2 |
+
|
3 |
+
# Variables
|
4 |
+
PYTHON_VERSION := 3.12
|
5 |
+
PYTHON := python$(PYTHON_VERSION)
|
6 |
+
PIP := pip3
|
7 |
+
VENV_NAME := venv
|
8 |
+
VENV_BIN := $(VENV_NAME)/bin
|
9 |
+
VENV_PYTHON := $(VENV_BIN)/python
|
10 |
+
VENV_PIP := $(VENV_BIN)/pip
|
11 |
+
PORT := 8501
|
12 |
+
|
13 |
+
# Default target
|
14 |
+
.DEFAULT_GOAL := help
|
15 |
+
|
16 |
+
help: ## Show this help message
|
17 |
+
@echo 'Usage:'
|
18 |
+
@echo ' make <target>'
|
19 |
+
@echo ''
|
20 |
+
@echo 'Targets:'
|
21 |
+
@awk -F ':|##' '/^[^\t].+?:.*?##/ { printf " %-20s %s\n", $$1, $$NF }' $(MAKEFILE_LIST)
|
22 |
+
|
23 |
+
check-python: ## Verify Python version
|
24 |
+
@which $(PYTHON) > /dev/null || (echo "Python $(PYTHON_VERSION) not found. Please install it first." && exit 1)
|
25 |
+
|
26 |
+
$(VENV_NAME): check-python ## Create virtual environment if it doesn't exist
|
27 |
+
$(PYTHON) -m venv $(VENV_NAME)
|
28 |
+
$(VENV_PIP) install --upgrade pip
|
29 |
+
|
30 |
+
install-dev: $(VENV_NAME) ## Install development dependencies
|
31 |
+
$(VENV_PIP) install -r requirements/dev.txt
|
32 |
+
|
33 |
+
install-prod: $(VENV_NAME) ## Install production dependencies
|
34 |
+
$(VENV_PIP) install -r requirements/prod.txt
|
35 |
+
|
36 |
+
run: ## Run Streamlit application
|
37 |
+
PYTHONPATH=. $(VENV_BIN)/streamlit run main.py --server.port $(PORT)
|
38 |
+
|
39 |
+
clean: ## Clean cache files and remove virtual environment
|
40 |
+
find . -type d -name "__pycache__" -exec rm -rf {} +
|
41 |
+
find . -type d -name ".pytest_cache" -exec rm -rf {} +
|
42 |
+
find . -type f -name "*.pyc" -delete
|
43 |
+
rm -rf $(VENV_NAME)
|
44 |
+
|
45 |
+
lint: ## Run linter
|
46 |
+
$(VENV_PIP) install pylint
|
47 |
+
$(VENV_BIN)/pylint src
|
48 |
+
|
49 |
+
format: ## Format code using black
|
50 |
+
$(VENV_PIP) install black
|
51 |
+
$(VENV_BIN)/black src
|
52 |
+
|
53 |
+
test: ## Run tests
|
54 |
+
$(VENV_BIN)/pytest tests
|
55 |
+
|
56 |
+
setup-dev: clean $(VENV_NAME) install-dev ## Clean existing setup and create fresh development installation
|
57 |
+
|
58 |
+
setup-prod: clean $(VENV_NAME) install-prod ## Clean existing setup and create fresh production installation
|
README.md
CHANGED
@@ -1,14 +1,91 @@
|
|
1 |
---
|
2 |
-
title:
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: streamlit
|
7 |
sdk_version: 1.41.0
|
8 |
-
app_file:
|
9 |
pinned: false
|
10 |
license: mit
|
11 |
short_description: AI Phone Leaderboard
|
12 |
---
|
13 |
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
+
title: AI Phone Leaderboard
|
3 |
+
emoji: 📱
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: purple
|
6 |
sdk: streamlit
|
7 |
sdk_version: 1.41.0
|
8 |
+
app_file: main.py
|
9 |
pinned: false
|
10 |
license: mit
|
11 |
short_description: AI Phone Leaderboard
|
12 |
---
|
13 |
|
14 |
+
# AI Phone Benchmark Leaderboard
|
15 |
+
|
16 |
+
Streamlit frontend for displaying AI model performance benchmarks across different mobile devices.
|
17 |
+
|
18 |
+
## Features
|
19 |
+
|
20 |
+
- Interactive data filtering
|
21 |
+
- Performance comparison charts
|
22 |
+
- Detailed benchmark leaderboard
|
23 |
+
- Real-time data updates
|
24 |
+
- Responsive design
|
25 |
+
|
26 |
+
## Local Development
|
27 |
+
|
28 |
+
1. Create a virtual environment:
|
29 |
+
```bash
|
30 |
+
make venv
|
31 |
+
```
|
32 |
+
|
33 |
+
2. Install dependencies:
|
34 |
+
```bash
|
35 |
+
make setup-dev
|
36 |
+
```
|
37 |
+
|
38 |
+
3. Set up environment variables:
|
39 |
+
Create a `.env` file:
|
40 |
+
```env
|
41 |
+
HF_TOKEN=your_hugging_face_token_here # Required for accessing the API
|
42 |
+
```
|
43 |
+
|
44 |
+
4. Run the application:
|
45 |
+
```bash
|
46 |
+
make run
|
47 |
+
```
|
48 |
+
|
49 |
+
## Deployment
|
50 |
+
|
51 |
+
This application is configured for deployment on Hugging Face Spaces:
|
52 |
+
|
53 |
+
1. Create a new Space:
|
54 |
+
- Go to huggingface.co/spaces
|
55 |
+
- Click "Create new Space"
|
56 |
+
- Select "Streamlit" as the SDK
|
57 |
+
- Choose a name for your space
|
58 |
+
|
59 |
+
2. Add required secret:
|
60 |
+
- Go to Space Settings
|
61 |
+
- Under "Repository Secrets"
|
62 |
+
- Add `HF_TOKEN` with your Hugging Face token
|
63 |
+
|
64 |
+
3. The application will automatically deploy when you push to the repository.
|
65 |
+
|
66 |
+
## Configuration
|
67 |
+
|
68 |
+
The application can be configured through environment variables:
|
69 |
+
- `HF_TOKEN`: Hugging Face access token (required)
|
70 |
+
- `API_URL`: Backend API URL (defaults to production)
|
71 |
+
|
72 |
+
## API Integration
|
73 |
+
|
74 |
+
The application integrates with the AI Phone Benchmark API for data retrieval:
|
75 |
+
- Leaderboard data
|
76 |
+
- Performance metrics
|
77 |
+
- Device information
|
78 |
+
|
79 |
+
## Development
|
80 |
+
|
81 |
+
Available make commands:
|
82 |
+
```bash
|
83 |
+
make help # Show available commands
|
84 |
+
make setup-dev # Setup development environment
|
85 |
+
make setup-prod # Setup production environment
|
86 |
+
make run # Run Streamlit application
|
87 |
+
make lint # Run code linter
|
88 |
+
make format # Format code using black
|
89 |
+
make test # Run tests
|
90 |
+
make clean # Clean cache files
|
91 |
+
```
|
main.py
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import asyncio
|
3 |
+
from src.core.styles import CUSTOM_CSS
|
4 |
+
from src.components.header import render_header
|
5 |
+
from src.components.filters import render_table_filters, render_plot_filters
|
6 |
+
from src.components.visualizations import (
|
7 |
+
render_performance_plots,
|
8 |
+
render_leaderboard_table,
|
9 |
+
)
|
10 |
+
from src.services.api import fetch_leaderboard_data
|
11 |
+
|
12 |
+
# Configure the page
|
13 |
+
st.set_page_config(
|
14 |
+
page_title="AI-Phone Leaderboard",
|
15 |
+
page_icon="src/static/images/favicon.png",
|
16 |
+
layout="wide",
|
17 |
+
initial_sidebar_state="expanded",
|
18 |
+
)
|
19 |
+
|
20 |
+
# Apply custom CSS
|
21 |
+
st.markdown(CUSTOM_CSS, unsafe_allow_html=True)
|
22 |
+
|
23 |
+
async def main():
|
24 |
+
# Render header
|
25 |
+
render_header()
|
26 |
+
|
27 |
+
# Fetch initial data
|
28 |
+
full_df = await fetch_leaderboard_data()
|
29 |
+
if full_df.empty:
|
30 |
+
st.info("No benchmark data available yet!")
|
31 |
+
return
|
32 |
+
|
33 |
+
# Get unique values for filters
|
34 |
+
models = sorted(full_df["Model"].unique())
|
35 |
+
benchmarks = sorted(full_df["Benchmark"].unique())
|
36 |
+
platforms = sorted(full_df["Platform"].unique())
|
37 |
+
devices = sorted(full_df["Normalized Device ID"].unique())
|
38 |
+
|
39 |
+
# Render table filters and get selections
|
40 |
+
(
|
41 |
+
selected_model_table,
|
42 |
+
selected_benchmark_table,
|
43 |
+
selected_platform_table,
|
44 |
+
selected_device_table,
|
45 |
+
) = render_table_filters(models, benchmarks, platforms, devices)
|
46 |
+
|
47 |
+
# Filter data for table
|
48 |
+
table_df = full_df.copy()
|
49 |
+
if selected_model_table != "All":
|
50 |
+
table_df = table_df[table_df["Model"] == selected_model_table]
|
51 |
+
if selected_benchmark_table != "All":
|
52 |
+
table_df = table_df[table_df["Benchmark"] == selected_benchmark_table]
|
53 |
+
if selected_platform_table != "All":
|
54 |
+
table_df = table_df[table_df["Platform"] == selected_platform_table]
|
55 |
+
if selected_device_table != "All":
|
56 |
+
table_df = table_df[table_df["Normalized Device ID"] == selected_device_table]
|
57 |
+
|
58 |
+
# Render leaderboard table
|
59 |
+
render_leaderboard_table(table_df)
|
60 |
+
|
61 |
+
# Performance plots section
|
62 |
+
st.subheader("Performance Comparison")
|
63 |
+
|
64 |
+
# Render plot filters and get selections
|
65 |
+
selected_model_plot, selected_benchmark_plot = render_plot_filters(
|
66 |
+
models, benchmarks
|
67 |
+
)
|
68 |
+
|
69 |
+
# Filter data for plots
|
70 |
+
plot_df = full_df[
|
71 |
+
(full_df["Model"] == selected_model_plot)
|
72 |
+
& (full_df["Benchmark"] == selected_benchmark_plot)
|
73 |
+
]
|
74 |
+
|
75 |
+
# Render performance plots
|
76 |
+
render_performance_plots(plot_df, selected_model_plot)
|
77 |
+
|
78 |
+
if __name__ == "__main__":
|
79 |
+
asyncio.run(main())
|
requirements/base.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit>=1.28.0
|
2 |
+
requests>=2.31.0
|
3 |
+
python-dotenv>=1.0.0
|
4 |
+
pandas>=2.1.3
|
5 |
+
plotly>=5.18.0
|
6 |
+
httpx>=0.25.1
|
7 |
+
pydantic-settings>=2.0.3
|
requirements/dev.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
-r base.txt
|
2 |
+
pytest>=7.4.3
|
3 |
+
black>=23.10.1
|
4 |
+
pylint>=3.0.2
|
5 |
+
pytest-cov>=4.1.0
|
requirements/prod.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
-r base.txt
|
src/__init__.py
ADDED
File without changes
|
src/app.py
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import asyncio
|
2 |
+
from typing import Optional
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
async def fetch_and_filter_data(
|
6 |
+
model_name: Optional[str] = None,
|
7 |
+
benchmark_label: Optional[str] = None
|
8 |
+
) -> pd.DataFrame:
|
9 |
+
"""Fetch and filter data based on parameters"""
|
10 |
+
from .services.api import fetch_leaderboard_data
|
11 |
+
|
12 |
+
return await fetch_leaderboard_data(
|
13 |
+
model_name=model_name,
|
14 |
+
benchmark_label=benchmark_label
|
15 |
+
)
|
src/components/__init__.py
ADDED
File without changes
|
src/components/filters.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from typing import List, Tuple
|
3 |
+
|
4 |
+
def render_table_filters(
|
5 |
+
models: List[str],
|
6 |
+
benchmarks: List[str],
|
7 |
+
platforms: List[str],
|
8 |
+
devices: List[str]
|
9 |
+
) -> Tuple[str, str, str, str]:
|
10 |
+
"""Render and handle table filters"""
|
11 |
+
table_filters = st.container()
|
12 |
+
with table_filters:
|
13 |
+
t1, t2, t3, t4 = st.columns(4)
|
14 |
+
with t1:
|
15 |
+
selected_model = st.selectbox(
|
16 |
+
"Model", ["All"] + list(models), key="table_model"
|
17 |
+
)
|
18 |
+
with t2:
|
19 |
+
selected_benchmark = st.selectbox(
|
20 |
+
"Benchmark", ["All"] + list(benchmarks), key="table_benchmark"
|
21 |
+
)
|
22 |
+
with t3:
|
23 |
+
selected_platform = st.selectbox(
|
24 |
+
"Platform", ["All"] + list(platforms), key="table_platform"
|
25 |
+
)
|
26 |
+
with t4:
|
27 |
+
selected_device = st.selectbox(
|
28 |
+
"Device", ["All"] + list(devices), key="table_device"
|
29 |
+
)
|
30 |
+
|
31 |
+
return selected_model, selected_benchmark, selected_platform, selected_device
|
32 |
+
|
33 |
+
def render_plot_filters(
|
34 |
+
models: List[str],
|
35 |
+
benchmarks: List[str]
|
36 |
+
) -> Tuple[str, str]:
|
37 |
+
"""Render and handle plot filters"""
|
38 |
+
plot_filters = st.container()
|
39 |
+
with plot_filters:
|
40 |
+
p1, p2 = st.columns(2)
|
41 |
+
with p1:
|
42 |
+
selected_model = st.selectbox(
|
43 |
+
"Model for Comparison", models, key="plot_model"
|
44 |
+
)
|
45 |
+
with p2:
|
46 |
+
selected_benchmark = st.selectbox(
|
47 |
+
"Benchmark for Comparison", benchmarks, key="plot_benchmark"
|
48 |
+
)
|
49 |
+
|
50 |
+
return selected_model, selected_benchmark
|
src/components/header.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import base64
|
3 |
+
from pathlib import Path
|
4 |
+
|
5 |
+
def get_image_base64(image_path: Path) -> str:
|
6 |
+
"""Convert image to base64 string"""
|
7 |
+
if not image_path.exists():
|
8 |
+
st.error(f"Logo not found at {image_path}")
|
9 |
+
return ""
|
10 |
+
|
11 |
+
with open(image_path, "rb") as img_file:
|
12 |
+
return base64.b64encode(img_file.read()).decode("utf-8")
|
13 |
+
|
14 |
+
def render_header():
|
15 |
+
"""Render the application header with logos"""
|
16 |
+
# Logo paths
|
17 |
+
hf_logo_path = Path("src/static/images/hf-logo.png")
|
18 |
+
pocketpal_logo_path = Path("src/static/images/pocketpal-ai-logo.png")
|
19 |
+
|
20 |
+
header_html = f"""
|
21 |
+
<div class="header-container">
|
22 |
+
<div class="logos-container">
|
23 |
+
<img src="data:image/png;base64,{get_image_base64(hf_logo_path)}" class="logo" alt="Hugging Face Logo">
|
24 |
+
<img src="data:image/png;base64,{get_image_base64(pocketpal_logo_path)}" class="logo pocketpal" alt="PocketPal AI Logo">
|
25 |
+
</div>
|
26 |
+
<h1 class="header-title">AI Phone Benchmark Leaderboard</h1>
|
27 |
+
<p class="header-subtitle">Comparing Large Language Models performance across AI Phones. Powered by PocketPal AI.</p>
|
28 |
+
</div>
|
29 |
+
"""
|
30 |
+
st.markdown(header_html, unsafe_allow_html=True)
|
src/components/visualizations.py
ADDED
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import plotly.express as px
|
3 |
+
import pandas as pd
|
4 |
+
from typing import Optional
|
5 |
+
|
6 |
+
def create_performance_plot(df: pd.DataFrame, metric: str, title: str):
|
7 |
+
"""Create a performance comparison plot"""
|
8 |
+
if df.empty:
|
9 |
+
return None
|
10 |
+
|
11 |
+
fig = px.bar(
|
12 |
+
df,
|
13 |
+
x="Device",
|
14 |
+
y=metric,
|
15 |
+
color="Platform",
|
16 |
+
title=title,
|
17 |
+
template="plotly_white",
|
18 |
+
barmode="group",
|
19 |
+
hover_data=["CPU Cores", "Memory Usage (GB)", "Model Size"],
|
20 |
+
)
|
21 |
+
fig.update_layout(
|
22 |
+
xaxis_title="Device",
|
23 |
+
yaxis_title="Time (ms)",
|
24 |
+
legend_title="Platform",
|
25 |
+
plot_bgcolor="white",
|
26 |
+
height=400,
|
27 |
+
)
|
28 |
+
return fig
|
29 |
+
|
30 |
+
def render_performance_plots(plot_df: pd.DataFrame, model_name: str):
|
31 |
+
"""Render performance comparison plots"""
|
32 |
+
if plot_df.empty:
|
33 |
+
st.warning(
|
34 |
+
"No data available for the selected model and benchmark combination."
|
35 |
+
)
|
36 |
+
return
|
37 |
+
|
38 |
+
col1, col2 = st.columns(2)
|
39 |
+
with col1:
|
40 |
+
fig1 = create_performance_plot(
|
41 |
+
plot_df,
|
42 |
+
"Prompt Processing",
|
43 |
+
f"Prompt Processing Time - {model_name}",
|
44 |
+
)
|
45 |
+
if fig1:
|
46 |
+
st.plotly_chart(fig1, use_container_width=True)
|
47 |
+
|
48 |
+
with col2:
|
49 |
+
fig2 = create_performance_plot(
|
50 |
+
plot_df,
|
51 |
+
"Token Generation",
|
52 |
+
f"Token Generation Time - {model_name}",
|
53 |
+
)
|
54 |
+
if fig2:
|
55 |
+
st.plotly_chart(fig2, use_container_width=True)
|
56 |
+
|
57 |
+
def render_leaderboard_table(df: pd.DataFrame):
|
58 |
+
"""Render the leaderboard table with grouped and formatted data"""
|
59 |
+
# Group and average the results
|
60 |
+
grouped_df = (
|
61 |
+
df.groupby(
|
62 |
+
["Model ID", "Benchmark", "Normalized Device ID", "Platform", "Device", "Model Size", "CPU Cores"]
|
63 |
+
)
|
64 |
+
.agg(
|
65 |
+
{
|
66 |
+
"Prompt Processing": ["mean", "count", "std"],
|
67 |
+
"Token Generation": ["mean", "std"],
|
68 |
+
}
|
69 |
+
)
|
70 |
+
.reset_index()
|
71 |
+
)
|
72 |
+
|
73 |
+
# Flatten column names
|
74 |
+
grouped_df.columns = [
|
75 |
+
col[0] if col[1] == "" else f"{col[0]} ({col[1]})" for col in grouped_df.columns
|
76 |
+
]
|
77 |
+
|
78 |
+
# Round numeric columns
|
79 |
+
numeric_cols = [
|
80 |
+
"Prompt Processing (mean)",
|
81 |
+
"Prompt Processing (std)",
|
82 |
+
"Token Generation (mean)",
|
83 |
+
"Token Generation (std)",
|
84 |
+
]
|
85 |
+
grouped_df[numeric_cols] = grouped_df[numeric_cols].round(2)
|
86 |
+
|
87 |
+
# Rename columns for display
|
88 |
+
grouped_df = grouped_df.rename(
|
89 |
+
columns={
|
90 |
+
"Prompt Processing (mean)": "PP Avg (s)",
|
91 |
+
"Prompt Processing (std)": "PP Std",
|
92 |
+
"Prompt Processing (count)": "Runs",
|
93 |
+
"Token Generation (mean)": "TG Avg (s)",
|
94 |
+
"Token Generation (std)": "TG Std",
|
95 |
+
}
|
96 |
+
)
|
97 |
+
|
98 |
+
# Reorder columns for display
|
99 |
+
display_cols = [
|
100 |
+
"Platform",
|
101 |
+
"Device",
|
102 |
+
"Model ID",
|
103 |
+
"Model Size",
|
104 |
+
"Benchmark",
|
105 |
+
"TG Avg (s)",
|
106 |
+
"TG Std",
|
107 |
+
"PP Avg (s)",
|
108 |
+
"PP Std",
|
109 |
+
]
|
110 |
+
|
111 |
+
# Display the filtered and grouped table
|
112 |
+
st.dataframe(
|
113 |
+
grouped_df[display_cols].sort_values(
|
114 |
+
["Model Size", "Benchmark", "TG Avg (s)"],
|
115 |
+
ascending=[False, True, True],
|
116 |
+
),
|
117 |
+
use_container_width=True,
|
118 |
+
height=400,
|
119 |
+
)
|
src/core/__init__.py
ADDED
File without changes
|
src/core/config.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pydantic_settings import BaseSettings
|
2 |
+
from functools import lru_cache
|
3 |
+
|
4 |
+
class Settings(BaseSettings):
|
5 |
+
API_URL: str = "https://a-ghorbani-ai-phone-benchmark-api.hf.space"
|
6 |
+
HF_TOKEN: str
|
7 |
+
|
8 |
+
class Config:
|
9 |
+
case_sensitive = True
|
10 |
+
env_file = ".env"
|
11 |
+
|
12 |
+
@lru_cache()
|
13 |
+
def get_settings():
|
14 |
+
return Settings()
|
15 |
+
|
16 |
+
settings = get_settings()
|
src/core/styles.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
CUSTOM_CSS = """
|
2 |
+
<style>
|
3 |
+
.stApp {
|
4 |
+
max-width: 1200px;
|
5 |
+
margin: 0 auto;
|
6 |
+
}
|
7 |
+
.plot-container {
|
8 |
+
background-color: white;
|
9 |
+
border-radius: 10px;
|
10 |
+
padding: 20px;
|
11 |
+
margin: 10px 0;
|
12 |
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
13 |
+
}
|
14 |
+
div[data-testid="stHorizontalBlock"] > div[data-testid="column"] {
|
15 |
+
background-color: #f8f9fa;
|
16 |
+
padding: 10px;
|
17 |
+
border-radius: 5px;
|
18 |
+
margin: 0 5px;
|
19 |
+
}
|
20 |
+
.header-container {
|
21 |
+
display: flex;
|
22 |
+
flex-direction: column;
|
23 |
+
align-items: center;
|
24 |
+
padding: 1rem 0 2rem 0;
|
25 |
+
}
|
26 |
+
.logos-container {
|
27 |
+
display: flex;
|
28 |
+
align-items: center;
|
29 |
+
justify-content: center;
|
30 |
+
gap: 2rem;
|
31 |
+
margin-bottom: 1rem;
|
32 |
+
height: 100px;
|
33 |
+
}
|
34 |
+
.logo {
|
35 |
+
width: 100px;
|
36 |
+
height: 100px;
|
37 |
+
object-fit: contain;
|
38 |
+
display: block;
|
39 |
+
}
|
40 |
+
.logo.pocketpal {
|
41 |
+
width: 80px;
|
42 |
+
height: 80px;
|
43 |
+
border-radius: 20px;
|
44 |
+
}
|
45 |
+
.header-title {
|
46 |
+
font-size: 2.5rem;
|
47 |
+
font-weight: 600;
|
48 |
+
text-align: center;
|
49 |
+
color: #1a1a1a;
|
50 |
+
margin: 0;
|
51 |
+
padding: 10px 0;
|
52 |
+
}
|
53 |
+
.header-subtitle {
|
54 |
+
font-size: 1.2rem;
|
55 |
+
text-align: center;
|
56 |
+
color: #666;
|
57 |
+
margin: 0;
|
58 |
+
padding-bottom: 1rem;
|
59 |
+
max-width: 800px;
|
60 |
+
}
|
61 |
+
</style>
|
62 |
+
"""
|
src/main.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Main module for the frontend application.
|
3 |
+
This file serves as a module init file.
|
4 |
+
"""
|
src/services/__init__.py
ADDED
File without changes
|
src/services/api.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import httpx
|
2 |
+
import pandas as pd
|
3 |
+
from typing import Optional, Dict
|
4 |
+
import streamlit as st
|
5 |
+
from src.core.config import settings
|
6 |
+
|
7 |
+
async def fetch_leaderboard_data(
|
8 |
+
model_name: Optional[str] = None,
|
9 |
+
benchmark_label: Optional[str] = None
|
10 |
+
) -> pd.DataFrame:
|
11 |
+
"""Fetch and process leaderboard data"""
|
12 |
+
params = {}
|
13 |
+
if model_name and model_name != "All":
|
14 |
+
params["model_name"] = model_name
|
15 |
+
if benchmark_label and benchmark_label != "All":
|
16 |
+
params["benchmark_label"] = benchmark_label
|
17 |
+
|
18 |
+
headers = {
|
19 |
+
"Authorization": f"Bearer {settings.HF_TOKEN}",
|
20 |
+
"Accept": "application/json"
|
21 |
+
}
|
22 |
+
|
23 |
+
try:
|
24 |
+
async with httpx.AsyncClient() as client:
|
25 |
+
response = await client.get(
|
26 |
+
f"{settings.API_URL}/api/v1/leaderboard",
|
27 |
+
params=params,
|
28 |
+
headers=headers,
|
29 |
+
follow_redirects=True
|
30 |
+
)
|
31 |
+
response.raise_for_status()
|
32 |
+
data = response.json()
|
33 |
+
return pd.DataFrame(data)
|
34 |
+
except Exception as e:
|
35 |
+
st.error(f"Error fetching data: {str(e)}")
|
36 |
+
if hasattr(e, 'response'):
|
37 |
+
st.error(f"Response: {e.response.text}")
|
38 |
+
return pd.DataFrame()
|
src/static/.DS_Store
ADDED
Binary file (6.15 kB). View file
|
|
src/static/images/favicon.png
ADDED
src/static/images/hf-logo.png
ADDED
src/static/images/pocketpal-ai-logo.png
ADDED