Alyafeai commited on
Commit
8eddc6c
·
1 Parent(s): b828f6c

fix some of the status

Browse files
Files changed (3) hide show
  1. app.py +20 -6
  2. backend/data_loader.py +63 -7
  3. frontend/leaderboard.html +51 -20
app.py CHANGED
@@ -34,11 +34,25 @@ def update_leaderboard_cache():
34
  if df.empty:
35
  GLOBAL_LEADERBOARD_DATA = []
36
  else:
37
- # Fill numeric NaNs with 0, string NaNs with ""
38
- df = df.fillna(0)
39
  df = df.drop(columns=["Model Size Filter"], errors="ignore")
 
 
 
 
 
 
 
40
  if "Model Size" in df.columns:
41
- df["Model Size"] = df["Model Size"].astype(float).round(2)
 
 
 
 
 
 
 
 
 
42
 
43
  # Update global variable
44
  GLOBAL_LEADERBOARD_DATA = df.drop(columns=["datetime"]).to_dict(orient="records")
@@ -122,8 +136,8 @@ async def get_model_likes(
122
  """Fetches the number of likes for a model from Hugging Face Hub."""
123
  try:
124
  info = API.model_info(repo_id=model_name, revision=revision, token=hf_api_token)
125
- likes = info.likes or 0
126
- downloads = info.downloads or 0
127
  return JSONResponse(content={"likes": likes, "downloads": downloads})
128
  except Exception as e:
129
  logging.error(f"Error fetching likes for {model_name}: {e}")
@@ -169,4 +183,4 @@ async def read_page(request: Request, page_name: str):
169
  return templates.TemplateResponse(page_name, {"request": request})
170
 
171
  if __name__ == "__main__":
172
- uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True, access_log=False)
 
34
  if df.empty:
35
  GLOBAL_LEADERBOARD_DATA = []
36
  else:
 
 
37
  df = df.drop(columns=["Model Size Filter"], errors="ignore")
38
+
39
+ # Keep scores numeric, but show Unknown for missing metadata fields.
40
+ score_cols = [t[2] for t in TASKS] + ["Average", "Rank"]
41
+ for col in score_cols:
42
+ if col in df.columns:
43
+ df[col] = pd.to_numeric(df[col], errors="coerce").fillna(0)
44
+
45
  if "Model Size" in df.columns:
46
+ size_series = pd.to_numeric(df["Model Size"], errors="coerce")
47
+ df["Model Size"] = size_series.apply(lambda v: int(v) if pd.notna(v) else "Unknown")
48
+
49
+ if "Hub ❤️" in df.columns:
50
+ likes_series = pd.to_numeric(df["Hub ❤️"], errors="coerce")
51
+ df["Hub ❤️"] = likes_series.apply(lambda v: int(v) if pd.notna(v) else "Unknown")
52
+
53
+ for col in ["License", "Revision", "Type", "Full Type", "Precision"]:
54
+ if col in df.columns:
55
+ df[col] = df[col].replace("", pd.NA).fillna("Unknown")
56
 
57
  # Update global variable
58
  GLOBAL_LEADERBOARD_DATA = df.drop(columns=["datetime"]).to_dict(orient="records")
 
136
  """Fetches the number of likes for a model from Hugging Face Hub."""
137
  try:
138
  info = API.model_info(repo_id=model_name, revision=revision, token=hf_api_token)
139
+ likes = info.likes
140
+ downloads = info.downloads
141
  return JSONResponse(content={"likes": likes, "downloads": downloads})
142
  except Exception as e:
143
  logging.error(f"Error fetching likes for {model_name}: {e}")
 
183
  return templates.TemplateResponse(page_name, {"request": request})
184
 
185
  if __name__ == "__main__":
186
+ uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=True, access_log=False)
backend/data_loader.py CHANGED
@@ -5,6 +5,7 @@ import os
5
  import contextlib
6
  import io
7
  import logging
 
8
  from pathlib import Path
9
  from typing import Dict, List, Any, Optional
10
 
@@ -13,12 +14,14 @@ import pandas as pd
13
  from huggingface_hub import snapshot_download
14
  from datetime import datetime
15
  from backend.config import (
 
16
  REQUESTS_REPO_ID,
17
  RESULTS_REPO_ID,
18
  TASKS,
19
  MODEL_TYPE_TO_EMOJI,
 
20
  )
21
- from backend.helpers import unify_precision
22
 
23
  logger = logging.getLogger(__name__)
24
 
@@ -97,7 +100,7 @@ def _parse_result_file(path: Path) -> Optional[Dict[str, Any]]:
97
  "Precision": precision,
98
  "datetime": datetime.strptime(string_date, "%Y-%m-%dT%H-%M-%S.%f")
99
  }
100
-
101
  for task_key, metric_key, display in TASKS:
102
  if isinstance(task_key,list):
103
  weight_total = 0
@@ -122,6 +125,33 @@ def _parse_result_file(path: Path) -> Optional[Dict[str, Any]]:
122
  return row
123
 
124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  def load_scoreboard() -> pd.DataFrame:
126
  """
127
  Main entrypoint used by the Space UI.
@@ -155,12 +185,36 @@ def load_scoreboard() -> pd.DataFrame:
155
  df[col] = (pd.to_numeric(df[col], errors="coerce") * 100).round(2)
156
  df["Average"] = df[task_cols].mean(axis=1).round(2)
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # merge metadata from finished requests
159
  finished = load_requests("finished")
160
  if not finished.empty:
161
  finished["precision"] = finished["precision"].apply(unify_precision)
162
  meta = finished.groupby(["model", "precision"]).last().reset_index()
163
 
 
 
 
164
  def enrich(row):
165
  m = meta[
166
  (meta["model"] == row["Model Name"]) &
@@ -168,13 +222,15 @@ def load_scoreboard() -> pd.DataFrame:
168
  ]
169
  if not m.empty:
170
  m = m.iloc[0]
171
- row["License"] = m.get("license", "UNK")
172
- row["Revision"] = m.get("revision", "UNK")
173
- row["Model Size"] = m.get("params", 0)
174
- row["Hub ❤️"] = m.get("likes", 0)
 
175
  row["Type"] = MODEL_TYPE_TO_EMOJI.get(
176
- m.get("model_type", ""), m.get("model_type", "")
177
  )
 
178
  return row
179
 
180
  df = df.apply(enrich, axis=1)
 
5
  import contextlib
6
  import io
7
  import logging
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
  from pathlib import Path
10
  from typing import Dict, List, Any, Optional
11
 
 
14
  from huggingface_hub import snapshot_download
15
  from datetime import datetime
16
  from backend.config import (
17
+ API,
18
  REQUESTS_REPO_ID,
19
  RESULTS_REPO_ID,
20
  TASKS,
21
  MODEL_TYPE_TO_EMOJI,
22
+ hf_api_token,
23
  )
24
+ from backend.helpers import unify_precision, get_model_size
25
 
26
  logger = logging.getLogger(__name__)
27
 
 
100
  "Precision": precision,
101
  "datetime": datetime.strptime(string_date, "%Y-%m-%dT%H-%M-%S.%f")
102
  }
103
+
104
  for task_key, metric_key, display in TASKS:
105
  if isinstance(task_key,list):
106
  weight_total = 0
 
125
  return row
126
 
127
 
128
+ def _fetch_hf_metadata(model_name: str) -> Dict[str, Any]:
129
+ try:
130
+ info = API.model_info(repo_id=model_name, token=hf_api_token)
131
+ except Exception as e:
132
+ logger.warning("Could not fetch HF metadata for '%s': %s", model_name, e)
133
+ return {}
134
+
135
+ card_data = getattr(info, "card_data", None)
136
+ if isinstance(card_data, dict):
137
+ license_name = card_data.get("license")
138
+ else:
139
+ license_name = getattr(card_data, "license", None)
140
+
141
+ model_size = get_model_size(model_info=info, precision="")
142
+ if model_size == 0:
143
+ safetensors = getattr(info, "safetensors", None)
144
+ if not safetensors or not safetensors.get("total"):
145
+ model_size = None
146
+
147
+ return {
148
+ "License": license_name,
149
+ "Revision": getattr(info, "sha", None),
150
+ "Model Size": model_size,
151
+ "Hub ❤️": getattr(info, "likes", None),
152
+ }
153
+
154
+
155
  def load_scoreboard() -> pd.DataFrame:
156
  """
157
  Main entrypoint used by the Space UI.
 
185
  df[col] = (pd.to_numeric(df[col], errors="coerce") * 100).round(2)
186
  df["Average"] = df[task_cols].mean(axis=1).round(2)
187
 
188
+ # metadata from Hugging Face API (fetched in parallel for speed)
189
+ model_names = df["Model Name"].dropna().unique().tolist()
190
+ hf_meta: Dict[str, Dict[str, Any]] = {}
191
+ if model_names:
192
+ max_workers = min(12, len(model_names))
193
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
194
+ future_to_model = {
195
+ executor.submit(_fetch_hf_metadata, model_name): model_name
196
+ for model_name in model_names
197
+ }
198
+ for future in as_completed(future_to_model):
199
+ model_name = future_to_model[future]
200
+ hf_meta[model_name] = future.result() or {}
201
+
202
+ df["License"] = df["Model Name"].map(lambda name: hf_meta.get(name, {}).get("License"))
203
+ df["Revision"] = df["Model Name"].map(lambda name: hf_meta.get(name, {}).get("Revision"))
204
+ df["Model Size"] = df["Model Name"].map(lambda name: hf_meta.get(name, {}).get("Model Size"))
205
+ df["Hub ❤️"] = df["Model Name"].map(lambda name: hf_meta.get(name, {}).get("Hub ❤️"))
206
+ df["Type"] = None
207
+ df["Full Type"] = None
208
+
209
  # merge metadata from finished requests
210
  finished = load_requests("finished")
211
  if not finished.empty:
212
  finished["precision"] = finished["precision"].apply(unify_precision)
213
  meta = finished.groupby(["model", "precision"]).last().reset_index()
214
 
215
+ def is_missing(v: Any) -> bool:
216
+ return v is None or (isinstance(v, str) and not v.strip()) or pd.isna(v)
217
+
218
  def enrich(row):
219
  m = meta[
220
  (meta["model"] == row["Model Name"]) &
 
222
  ]
223
  if not m.empty:
224
  m = m.iloc[0]
225
+ if is_missing(row.get("License")):
226
+ row["License"] = m.get("license")
227
+ if is_missing(row.get("Revision")):
228
+ row["Revision"] = m.get("revision")
229
+ model_type_raw = m.get("model_type", "Missing")
230
  row["Type"] = MODEL_TYPE_TO_EMOJI.get(
231
+ model_type_raw, model_type_raw
232
  )
233
+ row["Full Type"] = model_type_raw
234
  return row
235
 
236
  df = df.apply(enrich, axis=1)
frontend/leaderboard.html CHANGED
@@ -551,6 +551,19 @@
551
  const $ = s => document.querySelector(s);
552
  const $$ = s => [...document.querySelectorAll(s)];
553
  const EVAL_COLUMNS = window.EVAL_COLUMNS;
 
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
  let lbData = [], grid, maxMeta = 100, minMeta = 0, tableColumns = [];
556
  let currentSort = { colId: null, dir: 'none' };
@@ -565,9 +578,11 @@
565
  window.initLeaderboard = function (data) {
566
  lbData = data;
567
 
568
- const sizes = lbData.map(r => r["Model Size"] || 0);
569
- minMeta = sizes.length ? Math.ceil(Math.min(...sizes)) : 0;
570
- maxMeta = sizes.length ? Math.ceil(Math.max(...sizes)) : 100;
 
 
571
 
572
  if (!lbData.length) {
573
  $('#table-wrapper').innerHTML = "<div class='p-8 text-center text-slate-500'>No data loaded.</div>";
@@ -691,30 +706,34 @@
691
  `;
692
 
693
  // Reset values
 
 
 
 
694
  $('#modalRank').innerText = "#" + model["Rank"];
695
  $('#modalAvg').innerText = parseFloat(model["Average"]).toFixed(2);
696
- $('#modalSize').innerText = model["Model Size"] + "B";
697
- $('#modalLikes').innerText = "--";
698
  // We don't have an ID for downloads yet in the static HTML, so we rely on the injected HTML below
699
- $('#modalLicense').innerText = model["License"];
700
- $('#modalPrecision').innerText = model["Precision"];
701
- $('#modalRevision').innerText = model["Revision"];
702
 
703
  // --- 1. MODIFIED: Added Download Span to Metadata Line ---
704
  // I added the separator dot and the Downloads span at the end of this block
705
  const metadataHtml = `
706
- <span class="flex items-center gap-1" title="License"><i data-lucide="scale" class="w-3.5 h-3.5"></i> <span id="modalLicense">${model["License"]}</span></span>
707
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
708
 
709
- <span class="flex items-center gap-1" title="Precision"><i data-lucide="cpu" class="w-3.5 h-3.5"></i> <span id="modalPrecision">${model["Precision"]}</span></span>
710
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
711
 
712
- <span class="flex items-center gap-1" title="Revision"><i data-lucide="git-commit" class="w-3.5 h-3.5"></i> <span id="modalRevision" class="font-mono">${model["Revision"]}</span></span>
713
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
714
 
715
  <span class="flex items-center gap-1" title="Downloads (last 30 days)">
716
  <i data-lucide="download" class="w-3.5 h-3.5"></i>
717
- <span id="modalDownloads">--</span>
718
  </span>
719
  `;
720
 
@@ -724,7 +743,7 @@
724
  // --- Fetch Logic to include Downloads ---
725
  const formData = new FormData();
726
  formData.append('model_name', model["Model Name"]);
727
- formData.append('revision', model["Revision"]);
728
 
729
  fetch('/api/model-likes', {
730
  method: 'POST',
@@ -732,16 +751,21 @@
732
  })
733
  .then(response => response.json())
734
  .then(data => {
735
- if (data.likes !== undefined) {
736
- $('#modalLikes').innerText = data.likes;
737
  }
738
  // Check if API returns downloads and update
739
  if (data.downloads !== undefined) {
740
  const dl = document.getElementById('modalDownloads');
741
- if (dl) dl.innerText = data.downloads; // You might want to format this (e.g., 1.5k)
742
  }
743
  })
744
- .catch(error => console.error('Error fetching stats:', error));
 
 
 
 
 
745
 
746
  $('#modalLinkHF').href = `https://huggingface.co/${model["Model Name"]}`;
747
 
@@ -822,7 +846,11 @@
822
  // 1. Filter Data
823
  let filtered = lbData.filter(r =>
824
  (r["Model Name"] || "").toLowerCase().includes(sVal) &&
825
- (r["Model Size"] || 0) >= minSz && (r["Model Size"] || 0) <= maxSz &&
 
 
 
 
826
  (!precs.length || precs.includes(r["Precision"])) &&
827
  (!types.length || types.includes(r["Full Type"])) &&
828
  (!lics.length || lics.includes(r["License"]))
@@ -850,7 +878,7 @@
850
  if (!isNaN(nA) && !isNaN(nB)) {
851
  return currentSort.dir === 'asc' ? nA - nB : nB - nA;
852
  }
853
- return vA.toString().localeCompare(vB.toString()) * (currentSort.dir === 'asc' ? 1 : -1);
854
  });
855
  }
856
 
@@ -909,7 +937,10 @@
909
  };
910
 
911
  if (key === "Model Size") {
912
- def.formatter = (c) => gridjs.html(`<span class="font-mono">${c}B</span>`);
 
 
 
913
  }
914
 
915
  if (key === "Model Name") {
 
551
  const $ = s => document.querySelector(s);
552
  const $$ = s => [...document.querySelectorAll(s)];
553
  const EVAL_COLUMNS = window.EVAL_COLUMNS;
554
+ const toNumber = (v) => {
555
+ const n = Number.parseFloat(v);
556
+ return Number.isFinite(n) ? n : null;
557
+ };
558
+ const asUnknown = (v) => {
559
+ if (v === undefined || v === null) return "Unknown";
560
+ const s = String(v).trim();
561
+ return s ? s : "Unknown";
562
+ };
563
+ const prettyIntOrUnknown = (v) => {
564
+ const n = toNumber(v);
565
+ return n === null ? "Unknown" : String(Math.floor(n));
566
+ };
567
 
568
  let lbData = [], grid, maxMeta = 100, minMeta = 0, tableColumns = [];
569
  let currentSort = { colId: null, dir: 'none' };
 
578
  window.initLeaderboard = function (data) {
579
  lbData = data;
580
 
581
+ const sizes = lbData
582
+ .map(r => toNumber(r["Model Size"]))
583
+ .filter(v => v !== null);
584
+ minMeta = sizes.length ? Math.floor(Math.min(...sizes)) : 0;
585
+ maxMeta = sizes.length ? Math.floor(Math.max(...sizes)) : 100;
586
 
587
  if (!lbData.length) {
588
  $('#table-wrapper').innerHTML = "<div class='p-8 text-center text-slate-500'>No data loaded.</div>";
 
706
  `;
707
 
708
  // Reset values
709
+ const revision = asUnknown(model["Revision"]);
710
+ const revisionForApi = revision === "Unknown" ? "main" : revision;
711
+ const modelSize = toNumber(model["Model Size"]);
712
+ const fallbackLikes = model["Hub ❤️"];
713
  $('#modalRank').innerText = "#" + model["Rank"];
714
  $('#modalAvg').innerText = parseFloat(model["Average"]).toFixed(2);
715
+ $('#modalSize').innerText = modelSize === null ? "Unknown" : `${Math.floor(modelSize)}B`;
716
+ $('#modalLikes').innerText = prettyIntOrUnknown(fallbackLikes);
717
  // We don't have an ID for downloads yet in the static HTML, so we rely on the injected HTML below
718
+ $('#modalLicense').innerText = asUnknown(model["License"]);
719
+ $('#modalPrecision').innerText = asUnknown(model["Precision"]);
720
+ $('#modalRevision').innerText = revision;
721
 
722
  // --- 1. MODIFIED: Added Download Span to Metadata Line ---
723
  // I added the separator dot and the Downloads span at the end of this block
724
  const metadataHtml = `
725
+ <span class="flex items-center gap-1" title="License"><i data-lucide="scale" class="w-3.5 h-3.5"></i> <span id="modalLicense">${asUnknown(model["License"])}</span></span>
726
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
727
 
728
+ <span class="flex items-center gap-1" title="Precision"><i data-lucide="cpu" class="w-3.5 h-3.5"></i> <span id="modalPrecision">${asUnknown(model["Precision"])}</span></span>
729
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
730
 
731
+ <span class="flex items-center gap-1" title="Revision"><i data-lucide="git-commit" class="w-3.5 h-3.5"></i> <span id="modalRevision" class="font-mono">${revision}</span></span>
732
  <span class="w-1 h-1 rounded-full bg-slate-300 dark:bg-slate-600"></span>
733
 
734
  <span class="flex items-center gap-1" title="Downloads (last 30 days)">
735
  <i data-lucide="download" class="w-3.5 h-3.5"></i>
736
+ <span id="modalDownloads">Unknown</span>
737
  </span>
738
  `;
739
 
 
743
  // --- Fetch Logic to include Downloads ---
744
  const formData = new FormData();
745
  formData.append('model_name', model["Model Name"]);
746
+ formData.append('revision', revisionForApi);
747
 
748
  fetch('/api/model-likes', {
749
  method: 'POST',
 
751
  })
752
  .then(response => response.json())
753
  .then(data => {
754
+ if (data.likes !== undefined && data.likes !== null) {
755
+ $('#modalLikes').innerText = prettyIntOrUnknown(data.likes);
756
  }
757
  // Check if API returns downloads and update
758
  if (data.downloads !== undefined) {
759
  const dl = document.getElementById('modalDownloads');
760
+ if (dl) dl.innerText = prettyIntOrUnknown(data.downloads);
761
  }
762
  })
763
+ .catch(error => {
764
+ console.error('Error fetching stats:', error);
765
+ const dl = document.getElementById('modalDownloads');
766
+ if (dl) dl.innerText = "Unknown";
767
+ $('#modalLikes').innerText = prettyIntOrUnknown(fallbackLikes);
768
+ });
769
 
770
  $('#modalLinkHF').href = `https://huggingface.co/${model["Model Name"]}`;
771
 
 
846
  // 1. Filter Data
847
  let filtered = lbData.filter(r =>
848
  (r["Model Name"] || "").toLowerCase().includes(sVal) &&
849
+ (() => {
850
+ const s = toNumber(r["Model Size"]);
851
+ const sizeValue = s === null ? 0 : s;
852
+ return sizeValue >= minSz && sizeValue <= maxSz;
853
+ })() &&
854
  (!precs.length || precs.includes(r["Precision"])) &&
855
  (!types.length || types.includes(r["Full Type"])) &&
856
  (!lics.length || lics.includes(r["License"]))
 
878
  if (!isNaN(nA) && !isNaN(nB)) {
879
  return currentSort.dir === 'asc' ? nA - nB : nB - nA;
880
  }
881
+ return String(vA ?? "").localeCompare(String(vB ?? "")) * (currentSort.dir === 'asc' ? 1 : -1);
882
  });
883
  }
884
 
 
937
  };
938
 
939
  if (key === "Model Size") {
940
+ def.formatter = (c) => {
941
+ const n = toNumber(c);
942
+ return gridjs.html(`<span class="font-mono">${n === null ? "Unknown" : `${Math.floor(n)}B`}</span>`);
943
+ };
944
  }
945
 
946
  if (key === "Model Name") {