siro1 HF Staff commited on
Commit
6493bef
·
1 Parent(s): 568c204

Feat: cleanup

Browse files
Files changed (4) hide show
  1. app.py +109 -76
  2. src/envs.py +3 -5
  3. src/result.py +8 -4
  4. src/retrieve_data.py +10 -10
app.py CHANGED
@@ -1,54 +1,93 @@
 
1
  import gradio as gr
2
  import asyncio
3
  import time
4
  import threading
5
- from src.retrieve_data import populate_lb_data
 
 
 
 
6
 
7
- leaderboard_data = {}
8
 
 
 
9
 
10
- async def fetch_data():
11
- """Fetch the leaderboard data asynchronously"""
12
- global leaderboard_data
13
- try:
14
- data = await populate_lb_data()
15
- leaderboard_data = data
16
- return True
17
- except Exception as e:
18
- print(f"Error fetching data: {e}")
19
- return False
20
-
21
-
22
- def background_update():
23
- """Background thread function to update data every 5 minutes"""
24
- while True:
25
- print("Updating leaderboard data...")
26
- asyncio.run(fetch_data())
27
- time.sleep(300) # 5 minutes
28
-
29
-
30
- def create_table_for_lb(lb_name, gpu_name):
31
- """Create a formatted table for a specific leaderboard and GPU"""
32
- if (
33
- not lb_name
34
- or not gpu_name
35
- or lb_name not in leaderboard_data
36
- or gpu_name not in leaderboard_data[lb_name]
37
- ):
38
- return gr.Dataframe(
39
- headers=["Rank", "Submission Name", "User ID", "Score", "Date"],
40
- datatype=["number", "str", "str", "str", "str"],
41
- value=[],
42
- interactive=False,
43
- )
44
 
45
- lb_data = leaderboard_data[lb_name][gpu_name]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- headers = ["Rank", "Discord User ID", "Submission Name", "Score", "Date"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  rows = []
50
  for i, result in enumerate(lb_data.results, 1):
51
- # Add medal emoji for top 3 ranks
52
  rank_display = i
53
  if i == 1:
54
  rank_display = "🥇 1"
@@ -75,7 +114,7 @@ def create_table_for_lb(lb_name, gpu_name):
75
  "str",
76
  "str",
77
  "datetime",
78
- ], # Changed first column to str for medals
79
  value=rows,
80
  interactive=False,
81
  )
@@ -83,38 +122,27 @@ def create_table_for_lb(lb_name, gpu_name):
83
  return df
84
 
85
 
86
- def refresh_ui():
87
- """Force refresh the UI with latest data"""
88
- asyncio.run(fetch_data())
89
- return "Data refreshed!", get_lb_names(), [], None
90
-
91
-
92
- def get_lb_names():
93
- """Get list of available leaderboard names"""
94
- return list(leaderboard_data.keys())
95
-
96
-
97
- def get_gpu_names(lb_name):
98
- """Get list of available GPUs for a specific leaderboard"""
99
- if not lb_name or lb_name not in leaderboard_data:
100
- return []
101
- return list(leaderboard_data[lb_name].keys())
102
-
103
-
104
  def on_lb_change(lb_name):
105
- gpu_choices = get_gpu_names(lb_name)
 
 
 
 
106
 
107
  return (
108
  gr.update(choices=gpu_choices, value=gpu_choices[0] if gpu_choices else None),
109
- update_table(lb_name, gpu_choices[0]),
110
  )
111
 
112
 
113
  def update_table(lb_name, gpu_name):
114
- """Update the table based on selected leaderboard and GPU"""
115
- if not lb_name or not gpu_name:
116
  return None
117
- return create_table_for_lb(lb_name, gpu_name)
 
 
 
 
118
 
119
 
120
  def build_ui():
@@ -135,28 +163,30 @@ def build_ui():
135
  ) as app:
136
  gr.Markdown("# 🍿 KernelBot Leaderboard 🍿")
137
 
138
- asyncio.run(fetch_data())
 
 
 
 
 
139
 
140
  with gr.Row():
141
  with gr.Column(scale=1):
142
  lb_dropdown = gr.Dropdown(
143
- choices=get_lb_names(),
144
  label="Select Leaderboard",
145
  interactive=True,
 
146
  )
147
  gpu_dropdown = gr.Dropdown(
148
- choices=get_gpu_names(lb_dropdown.value),
149
  label="Select GPU",
150
  interactive=True,
 
151
  )
152
 
153
  with gr.Row():
154
- results_table = gr.Dataframe(
155
- headers=["Rank", "Submission Name", "User ID", "Score", "Date"],
156
- datatype=["number", "str", "str", "str", "str"],
157
- interactive=False,
158
- label="Results",
159
- )
160
 
161
  lb_dropdown.change(
162
  fn=on_lb_change,
@@ -172,7 +202,10 @@ def build_ui():
172
 
173
 
174
  if __name__ == "__main__":
175
- update_thread = threading.Thread(target=background_update, daemon=True)
176
- update_thread.start()
177
- app = build_ui()
178
- app.launch()
 
 
 
 
1
+ from typing import Callable
2
  import gradio as gr
3
  import asyncio
4
  import time
5
  import threading
6
+ from src.retrieve_data import (
7
+ get_gpus_for_leaderboard,
8
+ get_leaderboard_names,
9
+ get_leaderboard_submissions,
10
+ )
11
 
12
+ from src.envs import CACHE_TIMEOUT, BACKGROUND_REFRESH_INTERVAL
13
 
14
+ # key: func_name:args:kwargs, value: (timestamp, data)
15
+ cache = {}
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ active_selections = {
19
+ "leaderboard": None,
20
+ "gpu": None,
21
+ }
22
+
23
+ loop = asyncio.new_event_loop()
24
+ asyncio.set_event_loop(loop)
25
+
26
+ background_refresh_running = True
27
+
28
+
29
+ def cached_fetch(func: Callable, *args, force_refresh=False, **kwargs):
30
+ """Fetch data with caching to avoid redundant API calls"""
31
+ cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
32
+
33
+ current_time = time.time()
34
+
35
+ if not force_refresh and cache_key in cache:
36
+ timestamp, data = cache[cache_key]
37
+ if current_time - timestamp < CACHE_TIMEOUT:
38
+ return data
39
+
40
+ result = loop.run_until_complete(func(*args, **kwargs))
41
+ cache[cache_key] = (current_time, result)
42
+ return result
43
+
44
+
45
+ def invalidate_cache(prefix=None):
46
+ """Invalidate all cache entries or those matching a prefix"""
47
+ global cache
48
+ if prefix is None:
49
+ cache = {}
50
+ else:
51
+ cache = {k: v for k, v in cache.items() if not k.startswith(prefix)}
52
+
53
+
54
+ def background_refresh():
55
+ """Background thread to refresh active data periodically"""
56
+ while background_refresh_running:
57
+ try:
58
+ time.sleep(BACKGROUND_REFRESH_INTERVAL)
59
+
60
+ lb_name = active_selections["leaderboard"]
61
+ gpu_name = active_selections["gpu"]
62
+
63
+ if lb_name and gpu_name:
64
+ cached_fetch(
65
+ get_leaderboard_submissions, lb_name, gpu_name, force_refresh=True
66
+ )
67
+
68
+ cached_fetch(get_gpus_for_leaderboard, lb_name, force_refresh=True)
69
 
70
+ cached_fetch(get_leaderboard_names, force_refresh=True)
71
+
72
+ except Exception as e:
73
+ print(f"Background refresh error: {e}")
74
+
75
+
76
+ background_thread = threading.Thread(target=background_refresh, daemon=True)
77
+ background_thread.start()
78
+
79
+
80
+ def create_table_for_lb(lb_data):
81
+ headers = [
82
+ "Rank",
83
+ "Discord User ID",
84
+ "Submission Name",
85
+ "Runtime (ms)",
86
+ "Submission Date",
87
+ ]
88
 
89
  rows = []
90
  for i, result in enumerate(lb_data.results, 1):
 
91
  rank_display = i
92
  if i == 1:
93
  rank_display = "🥇 1"
 
114
  "str",
115
  "str",
116
  "datetime",
117
+ ],
118
  value=rows,
119
  interactive=False,
120
  )
 
122
  return df
123
 
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  def on_lb_change(lb_name):
126
+ gpu_choices = cached_fetch(get_gpus_for_leaderboard, lb_name)
127
+
128
+ active_selections["leaderboard"] = lb_name
129
+ if gpu_choices:
130
+ active_selections["gpu"] = gpu_choices[0]
131
 
132
  return (
133
  gr.update(choices=gpu_choices, value=gpu_choices[0] if gpu_choices else None),
134
+ update_table(lb_name, gpu_choices[0] if gpu_choices else None),
135
  )
136
 
137
 
138
  def update_table(lb_name, gpu_name):
139
+ if not gpu_name:
 
140
  return None
141
+
142
+ active_selections["gpu"] = gpu_name
143
+
144
+ data = cached_fetch(get_leaderboard_submissions, lb_name, gpu_name)
145
+ return create_table_for_lb(data)
146
 
147
 
148
  def build_ui():
 
163
  ) as app:
164
  gr.Markdown("# 🍿 KernelBot Leaderboard 🍿")
165
 
166
+ lb_names = cached_fetch(get_leaderboard_names)
167
+ selected_lb = lb_names[0]
168
+ gpu_names = cached_fetch(get_gpus_for_leaderboard, selected_lb)
169
+ selected_gpu = gpu_names[0]
170
+
171
+ data = cached_fetch(get_leaderboard_submissions, selected_lb, selected_gpu)
172
 
173
  with gr.Row():
174
  with gr.Column(scale=1):
175
  lb_dropdown = gr.Dropdown(
176
+ choices=lb_names,
177
  label="Select Leaderboard",
178
  interactive=True,
179
+ value=selected_lb,
180
  )
181
  gpu_dropdown = gr.Dropdown(
182
+ choices=gpu_names,
183
  label="Select GPU",
184
  interactive=True,
185
+ value=selected_gpu,
186
  )
187
 
188
  with gr.Row():
189
+ results_table = create_table_for_lb(data)
 
 
 
 
 
190
 
191
  lb_dropdown.change(
192
  fn=on_lb_change,
 
202
 
203
 
204
  if __name__ == "__main__":
205
+ try:
206
+ app = build_ui()
207
+ app.launch()
208
+ finally:
209
+ background_refresh_running = False
210
+ background_thread.join(timeout=1.0)
211
+ loop.close()
src/envs.py CHANGED
@@ -3,9 +3,7 @@ import os
3
 
4
  API_URL = os.getenv("API_URL", "http://localhost:8000")
5
  OWNER = "siro1"
 
6
 
7
- QUEUE_REPO = f"{OWNER}/requests"
8
- RESULTS_REPO = f"{OWNER}/results"
9
-
10
- CACHE_PATH = os.getenv("HF_HOME", ".")
11
-
 
3
 
4
  API_URL = os.getenv("API_URL", "http://localhost:8000")
5
  OWNER = "siro1"
6
+ TIMEOUT = 2000
7
 
8
+ CACHE_TIMEOUT = 300
9
+ BACKGROUND_REFRESH_INTERVAL = 240
 
 
 
src/result.py CHANGED
@@ -8,6 +8,7 @@ class Result:
8
  submission_name: str
9
  submission_time: datetime
10
  submission_score: float
 
11
  user_id: str
12
  rank: int
13
 
@@ -17,6 +18,7 @@ class Result:
17
  submission_name=data["submission_name"],
18
  submission_time=datetime.fromisoformat(data["submission_time"]),
19
  submission_score=data["submission_score"],
 
20
  user_id=data["user_id"],
21
  rank=data["rank"],
22
  )
@@ -29,10 +31,12 @@ class Result:
29
 
30
  def to_dict(self) -> dict:
31
  return {
32
- "filename": self.filename,
33
- "score": self.score,
34
- "user_name": self.user_name,
35
- "created_at": self.created_at.isoformat(),
 
 
36
  }
37
 
38
 
 
8
  submission_name: str
9
  submission_time: datetime
10
  submission_score: float
11
+ submission_id: str
12
  user_id: str
13
  rank: int
14
 
 
18
  submission_name=data["submission_name"],
19
  submission_time=datetime.fromisoformat(data["submission_time"]),
20
  submission_score=data["submission_score"],
21
+ submission_id=data["submission_id"],
22
  user_id=data["user_id"],
23
  rank=data["rank"],
24
  )
 
31
 
32
  def to_dict(self) -> dict:
33
  return {
34
+ "submission_name": self.submission_name,
35
+ "submission_time": self.submission_time.isoformat(),
36
+ "submission_score": self.submission_score,
37
+ "submission_id": self.submission_id,
38
+ "user_id": self.user_id,
39
+ "rank": self.rank,
40
  }
41
 
42
 
src/retrieve_data.py CHANGED
@@ -2,26 +2,26 @@ from collections import defaultdict
2
 
3
  from httpx import AsyncClient
4
 
5
- from src.envs import API_URL
6
  from src.result import LbData, Result
7
 
8
 
9
- async def get_leaderboards() -> list[str]:
10
- async with AsyncClient() as client:
11
  response = await client.get(f"{API_URL}/leaderboards")
12
  response.raise_for_status()
13
  return [lb["name"] for lb in response.json()]
14
 
15
 
16
- async def get_lb_gpus(lb_name: str) -> list[str]:
17
- async with AsyncClient() as client:
18
  response = await client.get(f"{API_URL}/gpus/{lb_name}")
19
  response.raise_for_status()
20
  return response.json()
21
 
22
 
23
- async def get_submissions(lb_name: str, gpu: str) -> LbData:
24
- async with AsyncClient() as client:
25
  response = await client.get(f"{API_URL}/submissions/{lb_name}/{gpu}")
26
  response.raise_for_status()
27
  return LbData(
@@ -33,11 +33,11 @@ async def get_submissions(lb_name: str, gpu: str) -> LbData:
33
 
34
  async def populate_lb_data():
35
  leaderboards: dict[str, dict[str, LbData]] = defaultdict(dict)
36
- lb_names = await get_leaderboards()
37
  for lb_name in lb_names:
38
- gpus = await get_lb_gpus(lb_name)
39
  for gpu in gpus:
40
- lb_data = await get_submissions(lb_name, gpu)
41
  leaderboards[lb_name][gpu] = lb_data
42
 
43
  return leaderboards
 
2
 
3
  from httpx import AsyncClient
4
 
5
+ from src.envs import API_URL, TIMEOUT
6
  from src.result import LbData, Result
7
 
8
 
9
+ async def get_leaderboard_names() -> list[str]:
10
+ async with AsyncClient(timeout=TIMEOUT) as client:
11
  response = await client.get(f"{API_URL}/leaderboards")
12
  response.raise_for_status()
13
  return [lb["name"] for lb in response.json()]
14
 
15
 
16
+ async def get_gpus_for_leaderboard(lb_name: str) -> list[str]:
17
+ async with AsyncClient(timeout=TIMEOUT) as client:
18
  response = await client.get(f"{API_URL}/gpus/{lb_name}")
19
  response.raise_for_status()
20
  return response.json()
21
 
22
 
23
+ async def get_leaderboard_submissions(lb_name: str, gpu: str) -> LbData:
24
+ async with AsyncClient(timeout=TIMEOUT) as client:
25
  response = await client.get(f"{API_URL}/submissions/{lb_name}/{gpu}")
26
  response.raise_for_status()
27
  return LbData(
 
33
 
34
  async def populate_lb_data():
35
  leaderboards: dict[str, dict[str, LbData]] = defaultdict(dict)
36
+ lb_names = await get_leaderboard_names()
37
  for lb_name in lb_names:
38
+ gpus = await get_gpus_for_leaderboard(lb_name)
39
  for gpu in gpus:
40
+ lb_data = await get_leaderboard_submissions(lb_name, gpu)
41
  leaderboards[lb_name][gpu] = lb_data
42
 
43
  return leaderboards