Upload 3 files
Browse files- app_token_auth.py +417 -0
- test_token_auth.py +58 -0
- token_auth.py +403 -0
app_token_auth.py
ADDED
@@ -0,0 +1,417 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Main application for Dynamic Highscores system.
|
3 |
+
|
4 |
+
This file integrates all components into a unified application.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import gradio as gr
|
9 |
+
import threading
|
10 |
+
import time
|
11 |
+
from database_schema import DynamicHighscoresDB
|
12 |
+
from auth import HuggingFaceAuth
|
13 |
+
from benchmark_selection import BenchmarkSelector, create_benchmark_selection_ui
|
14 |
+
from evaluation_queue import EvaluationQueue, create_model_submission_ui
|
15 |
+
from leaderboard import Leaderboard, create_leaderboard_ui
|
16 |
+
from sample_benchmarks import add_sample_benchmarks
|
17 |
+
|
18 |
+
# Initialize components in main thread
|
19 |
+
db = DynamicHighscoresDB()
|
20 |
+
auth_manager = HuggingFaceAuth(db)
|
21 |
+
benchmark_selector = BenchmarkSelector(db, auth_manager)
|
22 |
+
evaluation_queue = EvaluationQueue(db, auth_manager)
|
23 |
+
leaderboard = Leaderboard(db)
|
24 |
+
|
25 |
+
# Initialize sample benchmarks if none exist
|
26 |
+
print("Checking for existing benchmarks...")
|
27 |
+
benchmarks = db.get_benchmarks()
|
28 |
+
if not benchmarks or len(benchmarks) == 0:
|
29 |
+
print("No benchmarks found. Adding sample benchmarks...")
|
30 |
+
try:
|
31 |
+
# Make sure the database path is clear
|
32 |
+
print(f"Database path: {db.db_path}")
|
33 |
+
|
34 |
+
# Import and call the function directly
|
35 |
+
num_added = add_sample_benchmarks()
|
36 |
+
print(f"Added {num_added} sample benchmarks.")
|
37 |
+
except Exception as e:
|
38 |
+
print(f"Error adding sample benchmarks: {str(e)}")
|
39 |
+
# Try direct DB insertion as fallback
|
40 |
+
try:
|
41 |
+
print("Attempting direct benchmark insertion...")
|
42 |
+
db.add_benchmark(
|
43 |
+
name="MMLU (Massive Multitask Language Understanding)",
|
44 |
+
dataset_id="cais/mmlu",
|
45 |
+
description="Tests knowledge across 57 subjects"
|
46 |
+
)
|
47 |
+
print("Added fallback benchmark.")
|
48 |
+
except Exception as inner_e:
|
49 |
+
print(f"Fallback insertion failed: {str(inner_e)}")
|
50 |
+
else:
|
51 |
+
print(f"Found {len(benchmarks)} existing benchmarks.")
|
52 |
+
|
53 |
+
# Custom CSS with theme awareness
|
54 |
+
css = """
|
55 |
+
/* Theme-adaptive colored info box */
|
56 |
+
.info-text {
|
57 |
+
background-color: rgba(53, 130, 220, 0.1);
|
58 |
+
padding: 12px;
|
59 |
+
border-radius: 8px;
|
60 |
+
border-left: 4px solid #3498db;
|
61 |
+
margin: 12px 0;
|
62 |
+
}
|
63 |
+
|
64 |
+
/* High-contrast text for elements - works in light and dark themes */
|
65 |
+
.info-text, .header, .footer, .tab-content,
|
66 |
+
button, input, textarea, select, option,
|
67 |
+
.gradio-container *, .markdown-text {
|
68 |
+
color: var(--text-color, inherit) !important;
|
69 |
+
}
|
70 |
+
|
71 |
+
/* Container styling */
|
72 |
+
.container {
|
73 |
+
max-width: 1200px;
|
74 |
+
margin: 0 auto;
|
75 |
+
}
|
76 |
+
|
77 |
+
/* Header styling */
|
78 |
+
.header {
|
79 |
+
text-align: center;
|
80 |
+
margin-bottom: 20px;
|
81 |
+
font-weight: bold;
|
82 |
+
font-size: 24px;
|
83 |
+
}
|
84 |
+
|
85 |
+
/* Footer styling */
|
86 |
+
.footer {
|
87 |
+
text-align: center;
|
88 |
+
margin-top: 40px;
|
89 |
+
padding: 20px;
|
90 |
+
border-top: 1px solid var(--border-color-primary, #eee);
|
91 |
+
}
|
92 |
+
|
93 |
+
/* Login section styling */
|
94 |
+
.login-section {
|
95 |
+
padding: 10px;
|
96 |
+
margin-bottom: 15px;
|
97 |
+
border-radius: 8px;
|
98 |
+
background-color: rgba(250, 250, 250, 0.1);
|
99 |
+
text-align: center;
|
100 |
+
}
|
101 |
+
|
102 |
+
/* Token input styling */
|
103 |
+
.token-input {
|
104 |
+
margin: 10px 0;
|
105 |
+
padding: 8px;
|
106 |
+
border-radius: 4px;
|
107 |
+
border: 1px solid #ccc;
|
108 |
+
width: 100%;
|
109 |
+
}
|
110 |
+
|
111 |
+
/* Force high contrast on specific input areas */
|
112 |
+
input[type="text"], input[type="password"], textarea {
|
113 |
+
background-color: var(--background-fill-primary) !important;
|
114 |
+
color: var(--body-text-color) !important;
|
115 |
+
}
|
116 |
+
|
117 |
+
/* Force text visibility in multiple contexts */
|
118 |
+
.gradio-markdown p, .gradio-markdown h1, .gradio-markdown h2,
|
119 |
+
.gradio-markdown h3, .gradio-markdown h4, .gradio-markdown li {
|
120 |
+
color: var(--body-text-color) !important;
|
121 |
+
}
|
122 |
+
|
123 |
+
/* Fix dark mode text visibility */
|
124 |
+
@media (prefers-color-scheme: dark) {
|
125 |
+
input, textarea, select {
|
126 |
+
color: #ffffff !important;
|
127 |
+
}
|
128 |
+
|
129 |
+
::placeholder {
|
130 |
+
color: rgba(255, 255, 255, 0.5) !important;
|
131 |
+
}
|
132 |
+
}
|
133 |
+
"""
|
134 |
+
|
135 |
+
# Create token input UI
|
136 |
+
def create_token_input_ui():
|
137 |
+
with gr.Row():
|
138 |
+
with gr.Column():
|
139 |
+
gr.Markdown("### HuggingFace Token Authentication")
|
140 |
+
gr.Markdown("""
|
141 |
+
Enter your HuggingFace tokens to use this application.
|
142 |
+
You can find your tokens in your [HuggingFace settings](https://huggingface.co/settings/tokens).
|
143 |
+
|
144 |
+
- **Read Token**: Required for accessing models and datasets
|
145 |
+
- **Write Token**: Required for submitting evaluation results
|
146 |
+
|
147 |
+
Your tokens are stored only in your browser's local storage and are not saved on the server.
|
148 |
+
""")
|
149 |
+
|
150 |
+
read_token = gr.Textbox(
|
151 |
+
label="Read Token",
|
152 |
+
placeholder="Enter your HuggingFace read token",
|
153 |
+
type="password"
|
154 |
+
)
|
155 |
+
write_token = gr.Textbox(
|
156 |
+
label="Write Token",
|
157 |
+
placeholder="Enter your HuggingFace write token",
|
158 |
+
type="password"
|
159 |
+
)
|
160 |
+
save_button = gr.Button("Save Tokens")
|
161 |
+
clear_button = gr.Button("Clear Tokens")
|
162 |
+
token_status = gr.Markdown("Not authenticated")
|
163 |
+
|
164 |
+
# Hidden field to store the token status
|
165 |
+
token_state = gr.State(None)
|
166 |
+
|
167 |
+
# JavaScript to handle token storage
|
168 |
+
token_js = """
|
169 |
+
<script>
|
170 |
+
// Function to save tokens to localStorage
|
171 |
+
function saveTokens() {
|
172 |
+
const readToken = document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value;
|
173 |
+
const writeToken = document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value;
|
174 |
+
|
175 |
+
if (readToken && writeToken) {
|
176 |
+
localStorage.setItem("hf_read_token", readToken);
|
177 |
+
localStorage.setItem("hf_write_token", writeToken);
|
178 |
+
|
179 |
+
// Set token in cookie for server-side access
|
180 |
+
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict";
|
181 |
+
|
182 |
+
// Update status
|
183 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
184 |
+
if (statusElement) {
|
185 |
+
statusElement.textContent = "Authenticated with tokens";
|
186 |
+
statusElement.style.color = "green";
|
187 |
+
}
|
188 |
+
|
189 |
+
// Reload page to apply tokens
|
190 |
+
setTimeout(() => window.location.reload(), 1000);
|
191 |
+
} else {
|
192 |
+
alert("Please enter both read and write tokens");
|
193 |
+
}
|
194 |
+
}
|
195 |
+
|
196 |
+
// Function to clear tokens from localStorage
|
197 |
+
function clearTokens() {
|
198 |
+
localStorage.removeItem("hf_read_token");
|
199 |
+
localStorage.removeItem("hf_write_token");
|
200 |
+
|
201 |
+
// Clear token cookie
|
202 |
+
document.cookie = "hf_token=; path=/; max-age=0; SameSite=Strict";
|
203 |
+
|
204 |
+
// Update status
|
205 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
206 |
+
if (statusElement) {
|
207 |
+
statusElement.textContent = "Not authenticated";
|
208 |
+
statusElement.style.color = "red";
|
209 |
+
}
|
210 |
+
|
211 |
+
// Clear input fields
|
212 |
+
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = "";
|
213 |
+
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = "";
|
214 |
+
|
215 |
+
// Reload page to apply changes
|
216 |
+
setTimeout(() => window.location.reload(), 1000);
|
217 |
+
}
|
218 |
+
|
219 |
+
// Function to load tokens from localStorage
|
220 |
+
function loadTokens() {
|
221 |
+
const readToken = localStorage.getItem("hf_read_token");
|
222 |
+
const writeToken = localStorage.getItem("hf_write_token");
|
223 |
+
|
224 |
+
if (readToken && writeToken) {
|
225 |
+
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = readToken;
|
226 |
+
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = writeToken;
|
227 |
+
|
228 |
+
// Update status
|
229 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
230 |
+
if (statusElement) {
|
231 |
+
statusElement.textContent = "Authenticated with tokens";
|
232 |
+
statusElement.style.color = "green";
|
233 |
+
}
|
234 |
+
|
235 |
+
// Set token in cookie for server-side access if not already set
|
236 |
+
if (!document.cookie.includes("hf_token=")) {
|
237 |
+
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict";
|
238 |
+
}
|
239 |
+
}
|
240 |
+
}
|
241 |
+
|
242 |
+
// Add event listeners once DOM is loaded
|
243 |
+
document.addEventListener("DOMContentLoaded", function() {
|
244 |
+
// Load tokens from localStorage
|
245 |
+
loadTokens();
|
246 |
+
|
247 |
+
// Add event listeners to buttons
|
248 |
+
const saveButton = document.querySelector('button:nth-of-type(1)');
|
249 |
+
const clearButton = document.querySelector('button:nth-of-type(2)');
|
250 |
+
|
251 |
+
if (saveButton) {
|
252 |
+
saveButton.addEventListener("click", saveTokens);
|
253 |
+
}
|
254 |
+
|
255 |
+
if (clearButton) {
|
256 |
+
clearButton.addEventListener("click", clearTokens);
|
257 |
+
}
|
258 |
+
});
|
259 |
+
</script>
|
260 |
+
"""
|
261 |
+
|
262 |
+
return read_token, write_token, save_button, clear_button, token_status, token_state, token_js
|
263 |
+
|
264 |
+
# Simple manual authentication check
|
265 |
+
def check_user(request: gr.Request):
|
266 |
+
if request:
|
267 |
+
# Check for token in cookies
|
268 |
+
token = request.cookies.get("hf_token")
|
269 |
+
|
270 |
+
if token:
|
271 |
+
try:
|
272 |
+
# Validate token with HuggingFace
|
273 |
+
user_info = auth_manager.hf_api.whoami(token=token)
|
274 |
+
|
275 |
+
if user_info:
|
276 |
+
username = user_info.get("name", "")
|
277 |
+
print(f"User authenticated via token: {username}")
|
278 |
+
|
279 |
+
# Check if user exists in our database, create if not
|
280 |
+
user = db.get_user_by_username(username)
|
281 |
+
if not user:
|
282 |
+
# Create user if they don't exist
|
283 |
+
print(f"Creating new user: {username}")
|
284 |
+
is_admin = (username == "Quazim0t0")
|
285 |
+
db.add_user(username, username, is_admin)
|
286 |
+
user = db.get_user_by_username(username)
|
287 |
+
|
288 |
+
return username
|
289 |
+
except Exception as e:
|
290 |
+
print(f"Token validation error: {e}")
|
291 |
+
|
292 |
+
return None
|
293 |
+
|
294 |
+
# Start evaluation queue worker
|
295 |
+
def start_queue_worker():
|
296 |
+
# Wait a moment to ensure app is initialized
|
297 |
+
time.sleep(2)
|
298 |
+
try:
|
299 |
+
print("Starting evaluation queue worker...")
|
300 |
+
evaluation_queue.start_worker()
|
301 |
+
except Exception as e:
|
302 |
+
print(f"Error starting queue worker: {e}")
|
303 |
+
|
304 |
+
# Create Gradio app
|
305 |
+
with gr.Blocks(css=css, title="Dynamic Highscores") as app:
|
306 |
+
# State to track user
|
307 |
+
user_state = gr.State(None)
|
308 |
+
|
309 |
+
# Token input UI
|
310 |
+
read_token, write_token, save_button, clear_button, token_status, token_state, token_js = create_token_input_ui()
|
311 |
+
|
312 |
+
# Add the token handling JavaScript
|
313 |
+
gr.HTML(token_js)
|
314 |
+
|
315 |
+
gr.Markdown("# 🏆 Dynamic Highscores", elem_classes=["header"])
|
316 |
+
gr.Markdown("""
|
317 |
+
Welcome to Dynamic Highscores - a community benchmark platform for evaluating and comparing language models.
|
318 |
+
|
319 |
+
- **Add your own benchmarks** from HuggingFace datasets
|
320 |
+
- **Submit your models** for CPU-only evaluation
|
321 |
+
- **Compare performance** across different models and benchmarks
|
322 |
+
- **Filter results** by model type (Merge, Agent, Reasoning, Coding, etc.)
|
323 |
+
""", elem_classes=["info-text"])
|
324 |
+
|
325 |
+
# Main tabs
|
326 |
+
with gr.Tabs() as tabs:
|
327 |
+
with gr.TabItem("📊 Leaderboard", id=0):
|
328 |
+
leaderboard_ui = create_leaderboard_ui(leaderboard, db)
|
329 |
+
|
330 |
+
with gr.TabItem("🚀 Submit Model", id=1):
|
331 |
+
submission_ui = create_model_submission_ui(evaluation_queue, auth_manager, db)
|
332 |
+
|
333 |
+
with gr.TabItem("🔍 Benchmarks", id=2):
|
334 |
+
benchmark_ui = create_benchmark_selection_ui(benchmark_selector, auth_manager)
|
335 |
+
|
336 |
+
with gr.TabItem("🌐 Community Framework", id=3):
|
337 |
+
# Create a simple placeholder for the Community Framework tab
|
338 |
+
gr.Markdown("""
|
339 |
+
# 🌐 Dynamic Highscores Community Framework
|
340 |
+
|
341 |
+
## About Dynamic Highscores
|
342 |
+
|
343 |
+
Dynamic Highscores is an open-source community benchmark system for evaluating language models on any dataset. This project was created to fill the gap left by the retirement of HuggingFace's "Open LLM Leaderboards" which were discontinued due to outdated benchmarks.
|
344 |
+
|
345 |
+
### Key Features
|
346 |
+
|
347 |
+
- **Flexible Benchmarking**: Test models against any HuggingFace dataset, not just predefined benchmarks
|
348 |
+
- **Community-Driven**: Anyone can add new benchmarks and submit models for evaluation
|
349 |
+
- **Modern Evaluation**: Focus on contemporary benchmarks that better reflect current model capabilities
|
350 |
+
- **CPU-Only Evaluation**: Ensures fair comparisons across different models
|
351 |
+
- **Daily Submission Limits**: Prevents system abuse (one benchmark per day per user)
|
352 |
+
- **Model Tagging**: Categorize models as Merge, Agent, Reasoning, Coding, etc.
|
353 |
+
- **Unified Leaderboard**: View all models with filtering capabilities by tags
|
354 |
+
|
355 |
+
### Why This Project Matters
|
356 |
+
|
357 |
+
When HuggingFace retired their "Open LLM Leaderboards," the community lost a valuable resource for comparing model performance. The benchmarks used had become outdated and didn't reflect the rapid advances in language model capabilities.
|
358 |
+
|
359 |
+
Dynamic Highscores addresses this issue by allowing users to select from any benchmark on HuggingFace, including the most recent and relevant datasets. This ensures that models are evaluated on tasks that matter for current applications.
|
360 |
+
|
361 |
+
## Model Configuration System (Coming Soon)
|
362 |
+
|
363 |
+
We're working on a modular system for model configurations that will allow users to:
|
364 |
+
|
365 |
+
- Create and apply predefined configurations for different model types
|
366 |
+
- Define parameters such as Temperature, Top-K, Min-P, Top-P, and Repetition Penalty
|
367 |
+
- Share optimal configurations with the community
|
368 |
+
|
369 |
+
### Example Configuration (Gemma)
|
370 |
+
|
371 |
+
```
|
372 |
+
Temperature: 1.0
|
373 |
+
Top_K: 64
|
374 |
+
Min_P: 0.01
|
375 |
+
Top_P: 0.95
|
376 |
+
Repetition Penalty: 1.0
|
377 |
+
```
|
378 |
+
|
379 |
+
## Contributing to the Project
|
380 |
+
|
381 |
+
We welcome contributions from the community! If you'd like to improve Dynamic Highscores, here are some ways to get involved:
|
382 |
+
|
383 |
+
- **Add New Features**: Enhance the platform with additional functionality
|
384 |
+
- **Improve Evaluation Methods**: Help make model evaluations more accurate and efficient
|
385 |
+
- **Fix Bugs**: Address issues in the codebase
|
386 |
+
- **Enhance Documentation**: Make the project more accessible to new users
|
387 |
+
- **Add Model Configurations**: Contribute optimal configurations for different model types
|
388 |
+
|
389 |
+
To contribute, fork the repository, make your changes, and submit a pull request. We appreciate all contributions, big or small!
|
390 |
+
""")
|
391 |
+
|
392 |
+
gr.Markdown("""
|
393 |
+
### About Dynamic Highscores
|
394 |
+
|
395 |
+
This platform allows users to select benchmarks from HuggingFace datasets and evaluate models against them.
|
396 |
+
Each user can submit one benchmark per day (admin users are exempt from this limit).
|
397 |
+
All evaluations run on CPU only to ensure fair comparisons.
|
398 |
+
|
399 |
+
Created by Quazim0t0
|
400 |
+
""", elem_classes=["footer"])
|
401 |
+
|
402 |
+
# Check login on page load
|
403 |
+
app.load(
|
404 |
+
fn=check_user,
|
405 |
+
inputs=[],
|
406 |
+
outputs=[user_state]
|
407 |
+
)
|
408 |
+
|
409 |
+
# Launch the app
|
410 |
+
if __name__ == "__main__":
|
411 |
+
# Start queue worker in a separate thread
|
412 |
+
queue_thread = threading.Thread(target=start_queue_worker)
|
413 |
+
queue_thread.daemon = True
|
414 |
+
queue_thread.start()
|
415 |
+
|
416 |
+
# Launch the app
|
417 |
+
app.launch()
|
test_token_auth.py
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Test script for token-based authentication in Dynamic Highscores.
|
3 |
+
|
4 |
+
This script tests the token-based authentication functionality.
|
5 |
+
"""
|
6 |
+
|
7 |
+
import os
|
8 |
+
import sys
|
9 |
+
import requests
|
10 |
+
from huggingface_hub import HfApi
|
11 |
+
|
12 |
+
def test_token_auth():
|
13 |
+
"""Test token-based authentication with HuggingFace API."""
|
14 |
+
|
15 |
+
print("Testing token-based authentication...")
|
16 |
+
|
17 |
+
# Prompt for tokens
|
18 |
+
read_token = input("Enter your HuggingFace read token: ")
|
19 |
+
write_token = input("Enter your HuggingFace write token: ")
|
20 |
+
|
21 |
+
if not read_token or not write_token:
|
22 |
+
print("Error: Both read and write tokens are required.")
|
23 |
+
return False
|
24 |
+
|
25 |
+
# Initialize HuggingFace API
|
26 |
+
hf_api = HfApi()
|
27 |
+
|
28 |
+
# Test read token
|
29 |
+
print("\nTesting read token...")
|
30 |
+
try:
|
31 |
+
read_user_info = hf_api.whoami(token=read_token)
|
32 |
+
print(f"Read token valid. User: {read_user_info.get('name')}")
|
33 |
+
except Exception as e:
|
34 |
+
print(f"Error validating read token: {e}")
|
35 |
+
return False
|
36 |
+
|
37 |
+
# Test write token
|
38 |
+
print("\nTesting write token...")
|
39 |
+
try:
|
40 |
+
write_user_info = hf_api.whoami(token=write_token)
|
41 |
+
print(f"Write token valid. User: {write_user_info.get('name')}")
|
42 |
+
except Exception as e:
|
43 |
+
print(f"Error validating write token: {e}")
|
44 |
+
return False
|
45 |
+
|
46 |
+
# Check if tokens belong to the same user
|
47 |
+
if read_user_info.get("id") != write_user_info.get("id"):
|
48 |
+
print("Error: Tokens must belong to the same user.")
|
49 |
+
return False
|
50 |
+
|
51 |
+
print("\nToken authentication test successful!")
|
52 |
+
print(f"User: {read_user_info.get('name')}")
|
53 |
+
print(f"User ID: {read_user_info.get('id')}")
|
54 |
+
|
55 |
+
return True
|
56 |
+
|
57 |
+
if __name__ == "__main__":
|
58 |
+
test_token_auth()
|
token_auth.py
ADDED
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Authentication module for Dynamic Highscores system with token-based authentication.
|
3 |
+
|
4 |
+
This module handles user authentication with HuggingFace using direct token input,
|
5 |
+
user session management, and access control.
|
6 |
+
"""
|
7 |
+
|
8 |
+
import os
|
9 |
+
import json
|
10 |
+
import time
|
11 |
+
import requests
|
12 |
+
import gradio as gr
|
13 |
+
from huggingface_hub import HfApi, login
|
14 |
+
from functools import wraps
|
15 |
+
|
16 |
+
class HuggingFaceTokenAuth:
|
17 |
+
"""Authentication manager for HuggingFace integration using tokens."""
|
18 |
+
|
19 |
+
def __init__(self, db_manager):
|
20 |
+
"""Initialize the authentication manager.
|
21 |
+
|
22 |
+
Args:
|
23 |
+
db_manager: Database manager instance for user storage
|
24 |
+
"""
|
25 |
+
self.db_manager = db_manager
|
26 |
+
self.hf_api = HfApi()
|
27 |
+
self.admin_username = os.environ.get("ADMIN_USERNAME", "Quazim0t0")
|
28 |
+
self.running_in_space = 'SPACE_ID' in os.environ
|
29 |
+
|
30 |
+
print(f"Running in Space: {self.running_in_space}")
|
31 |
+
|
32 |
+
def validate_token(self, token):
|
33 |
+
"""Validate a HuggingFace token.
|
34 |
+
|
35 |
+
Args:
|
36 |
+
token: HuggingFace API token
|
37 |
+
|
38 |
+
Returns:
|
39 |
+
dict: User information if token is valid, None otherwise
|
40 |
+
"""
|
41 |
+
try:
|
42 |
+
# Validate token with HuggingFace
|
43 |
+
user_info = self.hf_api.whoami(token=token)
|
44 |
+
return user_info
|
45 |
+
except Exception as e:
|
46 |
+
print(f"Token validation error: {e}")
|
47 |
+
return None
|
48 |
+
|
49 |
+
def login_user(self, read_token, write_token=None):
|
50 |
+
"""Log in a user with their HuggingFace tokens.
|
51 |
+
|
52 |
+
Args:
|
53 |
+
read_token: HuggingFace read API token
|
54 |
+
write_token: HuggingFace write API token (optional)
|
55 |
+
|
56 |
+
Returns:
|
57 |
+
dict: User information if login successful, None otherwise
|
58 |
+
"""
|
59 |
+
try:
|
60 |
+
# Validate read token with HuggingFace
|
61 |
+
user_info = self.validate_token(read_token)
|
62 |
+
|
63 |
+
if not user_info:
|
64 |
+
return None
|
65 |
+
|
66 |
+
# Check if user exists in our database, create if not
|
67 |
+
username = user_info.get("name", user_info.get("fullname", ""))
|
68 |
+
hf_user_id = user_info.get("id", "")
|
69 |
+
|
70 |
+
if not hf_user_id:
|
71 |
+
return None
|
72 |
+
|
73 |
+
# Check if this is the admin account
|
74 |
+
is_admin = (username == self.admin_username)
|
75 |
+
|
76 |
+
# Add or get user from database
|
77 |
+
user_id = self.db_manager.add_user(username, hf_user_id, is_admin)
|
78 |
+
|
79 |
+
# Get complete user info from database
|
80 |
+
user = self.db_manager.get_user(hf_user_id)
|
81 |
+
|
82 |
+
if user:
|
83 |
+
# Add tokens to user info for session only (not stored in database)
|
84 |
+
user['read_token'] = read_token
|
85 |
+
if write_token:
|
86 |
+
user['write_token'] = write_token
|
87 |
+
return user
|
88 |
+
|
89 |
+
return None
|
90 |
+
except Exception as e:
|
91 |
+
print(f"Login error: {e}")
|
92 |
+
return None
|
93 |
+
|
94 |
+
def check_login(self, request: gr.Request):
|
95 |
+
"""Check if a user is logged in from a Gradio request.
|
96 |
+
|
97 |
+
Args:
|
98 |
+
request: Gradio request object
|
99 |
+
|
100 |
+
Returns:
|
101 |
+
dict: User information if logged in, None otherwise
|
102 |
+
"""
|
103 |
+
if not request:
|
104 |
+
return None
|
105 |
+
|
106 |
+
# Get token from cookies
|
107 |
+
token = request.cookies.get("hf_token")
|
108 |
+
|
109 |
+
if not token:
|
110 |
+
# Try to get token from headers
|
111 |
+
token = request.headers.get("HF-Token")
|
112 |
+
|
113 |
+
if not token:
|
114 |
+
return None
|
115 |
+
|
116 |
+
try:
|
117 |
+
# Validate token with HuggingFace
|
118 |
+
user_info = self.hf_api.whoami(token=token)
|
119 |
+
|
120 |
+
if not user_info:
|
121 |
+
return None
|
122 |
+
|
123 |
+
# Get user from database
|
124 |
+
hf_user_id = user_info.get("id", "")
|
125 |
+
user = self.db_manager.get_user(hf_user_id)
|
126 |
+
|
127 |
+
if user:
|
128 |
+
# Add token to user info for session only (not stored in database)
|
129 |
+
user['read_token'] = token
|
130 |
+
return user
|
131 |
+
|
132 |
+
return None
|
133 |
+
except Exception as e:
|
134 |
+
print(f"Check login error: {e}")
|
135 |
+
return None
|
136 |
+
|
137 |
+
def require_login(self, func):
|
138 |
+
"""Decorator to require login for a function.
|
139 |
+
|
140 |
+
Args:
|
141 |
+
func: Function to decorate
|
142 |
+
|
143 |
+
Returns:
|
144 |
+
Function: Decorated function that requires login
|
145 |
+
"""
|
146 |
+
@wraps(func)
|
147 |
+
def wrapper(*args, **kwargs):
|
148 |
+
# Find the request argument
|
149 |
+
request = None
|
150 |
+
for arg in args:
|
151 |
+
if isinstance(arg, gr.Request):
|
152 |
+
request = arg
|
153 |
+
break
|
154 |
+
|
155 |
+
if not request and 'request' in kwargs:
|
156 |
+
request = kwargs['request']
|
157 |
+
|
158 |
+
if not request:
|
159 |
+
return "Please enter your HuggingFace tokens to access this feature."
|
160 |
+
|
161 |
+
# Check if user is logged in
|
162 |
+
user = self.check_login(request)
|
163 |
+
|
164 |
+
if not user:
|
165 |
+
return "Please enter your HuggingFace tokens to access this feature."
|
166 |
+
|
167 |
+
# Add user to kwargs
|
168 |
+
kwargs['user'] = user
|
169 |
+
|
170 |
+
# Call the original function
|
171 |
+
return func(*args, **kwargs)
|
172 |
+
|
173 |
+
return wrapper
|
174 |
+
|
175 |
+
def require_admin(self, func):
|
176 |
+
"""Decorator to require admin privileges for a function.
|
177 |
+
|
178 |
+
Args:
|
179 |
+
func: Function to decorate
|
180 |
+
|
181 |
+
Returns:
|
182 |
+
Function: Decorated function that requires admin privileges
|
183 |
+
"""
|
184 |
+
@wraps(func)
|
185 |
+
def wrapper(*args, **kwargs):
|
186 |
+
# Find the request argument
|
187 |
+
request = None
|
188 |
+
for arg in args:
|
189 |
+
if isinstance(arg, gr.Request):
|
190 |
+
request = arg
|
191 |
+
break
|
192 |
+
|
193 |
+
if not request and 'request' in kwargs:
|
194 |
+
request = kwargs['request']
|
195 |
+
|
196 |
+
if not request:
|
197 |
+
return "Admin access required."
|
198 |
+
|
199 |
+
# Check if user is logged in
|
200 |
+
user = self.check_login(request)
|
201 |
+
|
202 |
+
if not user:
|
203 |
+
return "Admin access required."
|
204 |
+
|
205 |
+
# Check if user is admin
|
206 |
+
if not user.get('is_admin', False):
|
207 |
+
return "Admin access required."
|
208 |
+
|
209 |
+
# Add user to kwargs
|
210 |
+
kwargs['user'] = user
|
211 |
+
|
212 |
+
# Call the original function
|
213 |
+
return func(*args, **kwargs)
|
214 |
+
|
215 |
+
return wrapper
|
216 |
+
|
217 |
+
def can_submit_benchmark(self, user_id):
|
218 |
+
"""Check if a user can submit a benchmark today.
|
219 |
+
|
220 |
+
Args:
|
221 |
+
user_id: User ID to check
|
222 |
+
|
223 |
+
Returns:
|
224 |
+
bool: True if user can submit, False otherwise
|
225 |
+
"""
|
226 |
+
return self.db_manager.can_submit_today(user_id)
|
227 |
+
|
228 |
+
def update_submission_date(self, user_id):
|
229 |
+
"""Update the last submission date for a user.
|
230 |
+
|
231 |
+
Args:
|
232 |
+
user_id: User ID to update
|
233 |
+
"""
|
234 |
+
self.db_manager.update_submission_date(user_id)
|
235 |
+
|
236 |
+
# Token input UI components
|
237 |
+
def create_token_input_ui():
|
238 |
+
"""Create the token input UI components.
|
239 |
+
|
240 |
+
Returns:
|
241 |
+
tuple: (read_token, write_token, save_button, clear_button, token_status, token_state, token_js)
|
242 |
+
"""
|
243 |
+
with gr.Row():
|
244 |
+
with gr.Column():
|
245 |
+
gr.Markdown("### HuggingFace Token Authentication")
|
246 |
+
gr.Markdown("""
|
247 |
+
Enter your HuggingFace tokens to use this application.
|
248 |
+
You can find your tokens in your [HuggingFace settings](https://huggingface.co/settings/tokens).
|
249 |
+
|
250 |
+
- **Read Token**: Required for accessing models and datasets
|
251 |
+
- **Write Token**: Required for submitting evaluation results
|
252 |
+
|
253 |
+
Your tokens are stored only in your browser's local storage and are not saved on the server.
|
254 |
+
""")
|
255 |
+
|
256 |
+
read_token = gr.Textbox(
|
257 |
+
label="Read Token",
|
258 |
+
placeholder="Enter your HuggingFace read token",
|
259 |
+
type="password"
|
260 |
+
)
|
261 |
+
write_token = gr.Textbox(
|
262 |
+
label="Write Token",
|
263 |
+
placeholder="Enter your HuggingFace write token",
|
264 |
+
type="password"
|
265 |
+
)
|
266 |
+
save_button = gr.Button("Save Tokens")
|
267 |
+
clear_button = gr.Button("Clear Tokens")
|
268 |
+
token_status = gr.Markdown("Not authenticated")
|
269 |
+
|
270 |
+
# Hidden field to store the token status
|
271 |
+
token_state = gr.State(None)
|
272 |
+
|
273 |
+
# JavaScript to handle token storage
|
274 |
+
token_js = """
|
275 |
+
<script>
|
276 |
+
// Function to save tokens to localStorage
|
277 |
+
function saveTokens() {
|
278 |
+
const readToken = document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value;
|
279 |
+
const writeToken = document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value;
|
280 |
+
|
281 |
+
if (readToken && writeToken) {
|
282 |
+
localStorage.setItem("hf_read_token", readToken);
|
283 |
+
localStorage.setItem("hf_write_token", writeToken);
|
284 |
+
|
285 |
+
// Set token in cookie for server-side access
|
286 |
+
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict";
|
287 |
+
|
288 |
+
// Update status
|
289 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
290 |
+
if (statusElement) {
|
291 |
+
statusElement.textContent = "Authenticated with tokens";
|
292 |
+
statusElement.style.color = "green";
|
293 |
+
}
|
294 |
+
|
295 |
+
// Reload page to apply tokens
|
296 |
+
setTimeout(() => window.location.reload(), 1000);
|
297 |
+
} else {
|
298 |
+
alert("Please enter both read and write tokens");
|
299 |
+
}
|
300 |
+
}
|
301 |
+
|
302 |
+
// Function to clear tokens from localStorage
|
303 |
+
function clearTokens() {
|
304 |
+
localStorage.removeItem("hf_read_token");
|
305 |
+
localStorage.removeItem("hf_write_token");
|
306 |
+
|
307 |
+
// Clear token cookie
|
308 |
+
document.cookie = "hf_token=; path=/; max-age=0; SameSite=Strict";
|
309 |
+
|
310 |
+
// Update status
|
311 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
312 |
+
if (statusElement) {
|
313 |
+
statusElement.textContent = "Not authenticated";
|
314 |
+
statusElement.style.color = "red";
|
315 |
+
}
|
316 |
+
|
317 |
+
// Clear input fields
|
318 |
+
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = "";
|
319 |
+
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = "";
|
320 |
+
|
321 |
+
// Reload page to apply changes
|
322 |
+
setTimeout(() => window.location.reload(), 1000);
|
323 |
+
}
|
324 |
+
|
325 |
+
// Function to load tokens from localStorage
|
326 |
+
function loadTokens() {
|
327 |
+
const readToken = localStorage.getItem("hf_read_token");
|
328 |
+
const writeToken = localStorage.getItem("hf_write_token");
|
329 |
+
|
330 |
+
if (readToken && writeToken) {
|
331 |
+
document.querySelector('input[placeholder="Enter your HuggingFace read token"]').value = readToken;
|
332 |
+
document.querySelector('input[placeholder="Enter your HuggingFace write token"]').value = writeToken;
|
333 |
+
|
334 |
+
// Update status
|
335 |
+
const statusElement = document.querySelector('div[data-testid="markdown"] p');
|
336 |
+
if (statusElement) {
|
337 |
+
statusElement.textContent = "Authenticated with tokens";
|
338 |
+
statusElement.style.color = "green";
|
339 |
+
}
|
340 |
+
|
341 |
+
// Set token in cookie for server-side access if not already set
|
342 |
+
if (!document.cookie.includes("hf_token=")) {
|
343 |
+
document.cookie = "hf_token=" + readToken + "; path=/; SameSite=Strict";
|
344 |
+
}
|
345 |
+
}
|
346 |
+
}
|
347 |
+
|
348 |
+
// Add event listeners once DOM is loaded
|
349 |
+
document.addEventListener("DOMContentLoaded", function() {
|
350 |
+
// Load tokens from localStorage
|
351 |
+
loadTokens();
|
352 |
+
|
353 |
+
// Add event listeners to buttons
|
354 |
+
const saveButton = document.querySelector('button:nth-of-type(1)');
|
355 |
+
const clearButton = document.querySelector('button:nth-of-type(2)');
|
356 |
+
|
357 |
+
if (saveButton) {
|
358 |
+
saveButton.addEventListener("click", saveTokens);
|
359 |
+
}
|
360 |
+
|
361 |
+
if (clearButton) {
|
362 |
+
clearButton.addEventListener("click", clearTokens);
|
363 |
+
}
|
364 |
+
});
|
365 |
+
</script>
|
366 |
+
"""
|
367 |
+
|
368 |
+
return read_token, write_token, save_button, clear_button, token_status, token_state, token_js
|
369 |
+
|
370 |
+
def validate_tokens(auth_manager, read_token, write_token):
|
371 |
+
"""Validate HuggingFace tokens.
|
372 |
+
|
373 |
+
Args:
|
374 |
+
auth_manager: Authentication manager instance
|
375 |
+
read_token: HuggingFace read token
|
376 |
+
write_token: HuggingFace write token
|
377 |
+
|
378 |
+
Returns:
|
379 |
+
str: Status message
|
380 |
+
"""
|
381 |
+
if not read_token or not write_token:
|
382 |
+
return "Please enter both read and write tokens"
|
383 |
+
|
384 |
+
# Validate read token
|
385 |
+
read_user_info = auth_manager.validate_token(read_token)
|
386 |
+
if not read_user_info:
|
387 |
+
return "Invalid read token"
|
388 |
+
|
389 |
+
# Validate write token
|
390 |
+
write_user_info = auth_manager.validate_token(write_token)
|
391 |
+
if not write_user_info:
|
392 |
+
return "Invalid write token"
|
393 |
+
|
394 |
+
# Check if tokens belong to the same user
|
395 |
+
if read_user_info.get("id") != write_user_info.get("id"):
|
396 |
+
return "Tokens must belong to the same user"
|
397 |
+
|
398 |
+
# Login user with tokens
|
399 |
+
user = auth_manager.login_user(read_token, write_token)
|
400 |
+
if not user:
|
401 |
+
return "Failed to authenticate user"
|
402 |
+
|
403 |
+
return f"Authenticated as {user.get('username')}"
|