Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
-
# app.py
|
| 2 |
import os, time, html, logging, requests, threading
|
| 3 |
-
from typing import Any, Dict, List, Optional
|
| 4 |
from fastapi import FastAPI, HTTPException
|
| 5 |
from fastapi.responses import HTMLResponse, JSONResponse
|
| 6 |
|
|
@@ -49,6 +48,46 @@ def _get_with_backoff(url: str) -> Dict[str, Any]:
|
|
| 49 |
raise HTTPException(status_code=502, detail=f"Replicate error {r.status_code}: {r.text[:500]}")
|
| 50 |
raise HTTPException(status_code=429, detail="Too many retries after 429 responses.")
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
def _ensure_page_loaded(target_index: int) -> None:
|
| 53 |
"""
|
| 54 |
Ensure pages up to target_index (0-based) are loaded into _pages.
|
|
@@ -106,19 +145,48 @@ def _render_table(models: List[Dict[str, Any]]) -> str:
|
|
| 106 |
tags = m.get("tags") or m.get("categories") or []
|
| 107 |
tags_html = " ".join(f"<span class='tag'>{html.escape(str(t))}</span>" for t in tags[:8])
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
thumb_html = f"<img src='{html.escape(thumb)}' alt='' />" if thumb else ""
|
| 110 |
rows.append(
|
| 111 |
f"<tr>"
|
| 112 |
f"<td class='thumb'>{thumb_html}</td>"
|
| 113 |
-
f"<td class='id'>{mid}</td>"
|
| 114 |
f"<td class='desc'>{desc}</td>"
|
| 115 |
f"<td class='vis'>{vis}</td>"
|
|
|
|
|
|
|
| 116 |
f"<td class='tags'>{tags_html}</td>"
|
| 117 |
f"</tr>"
|
| 118 |
)
|
| 119 |
return (
|
| 120 |
"<table class='grid'>"
|
| 121 |
-
"<thead><tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
"<tbody>"
|
| 123 |
+ "\n".join(rows) +
|
| 124 |
"</tbody></table>"
|
|
@@ -187,6 +255,8 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
|
|
| 187 |
.id {{ font-weight: 600; white-space: nowrap; }}
|
| 188 |
.desc {{ color: #333; max-width: 700px; }}
|
| 189 |
.vis {{ white-space: nowrap; color: #555; }}
|
|
|
|
|
|
|
| 190 |
.tags .tag {{ display: inline-block; padding: 2px 8px; margin: 2px; background: #f2f2f7; border: 1px solid #e5e5ea; border-radius: 999px; font-size: 12px; color: #333; }}
|
| 191 |
.pagination {{ margin-top: 20px; }}
|
| 192 |
.page {{ display: inline-block; padding: 6px 10px; margin-right: 6px; border: 1px solid #ddd; border-radius: 6px; text-decoration: none; color: #333; }}
|
|
@@ -207,16 +277,13 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
|
|
| 207 |
<span class="pill">building: {building}</span>
|
| 208 |
<span class="pill">complete: {complete}</span>
|
| 209 |
</div>
|
| 210 |
-
|
| 211 |
<form class="searchbar" method="GET" action="/search" onsubmit="return onSearchSubmit();" >
|
| 212 |
<input id="q" name="q" type="text" placeholder="Search models, descriptions, tags" value="{html.escape(search_query)}" {search_disabled} />
|
| 213 |
<button id="qbtn" type="submit" {search_disabled}>Search</button>
|
| 214 |
<span id="searchHint" style="color:#666; font-size:12px;">{'' if _BUILD_COMPLETE else 'Search available after catalog completes.'}</span>
|
| 215 |
</form>
|
| 216 |
-
|
| 217 |
{html_table}
|
| 218 |
{_render_pagination(page_num)}
|
| 219 |
-
|
| 220 |
<script>
|
| 221 |
// Start building after the first render completes
|
| 222 |
document.addEventListener("DOMContentLoaded", function() {{
|
|
@@ -244,7 +311,6 @@ def _shell(page_index_0: int, html_table: str, *, search_query: str = "") -> str
|
|
| 244 |
}};
|
| 245 |
poll();
|
| 246 |
}});
|
| 247 |
-
|
| 248 |
function onSearchSubmit() {{
|
| 249 |
const q = document.getElementById("q");
|
| 250 |
return q && !q.disabled;
|
|
@@ -313,9 +379,4 @@ def search(q: str = ""):
|
|
| 313 |
base_models = _pages[idx]["results"] if _pages else []
|
| 314 |
if _BUILD_COMPLETE and q.strip():
|
| 315 |
results = _search_models(q)
|
| 316 |
-
content = _render_table(results
|
| 317 |
-
return HTMLResponse(_shell(idx, content, search_query=q))
|
| 318 |
-
else:
|
| 319 |
-
# Show page 1 with disabled search and a hint
|
| 320 |
-
content = _render_table(base_models)
|
| 321 |
-
return HTMLResponse(_shell(idx, content, search_query=q))
|
|
|
|
|
|
|
| 1 |
import os, time, html, logging, requests, threading
|
| 2 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 3 |
from fastapi import FastAPI, HTTPException
|
| 4 |
from fastapi.responses import HTMLResponse, JSONResponse
|
| 5 |
|
|
|
|
| 48 |
raise HTTPException(status_code=502, detail=f"Replicate error {r.status_code}: {r.text[:500]}")
|
| 49 |
raise HTTPException(status_code=429, detail="Too many retries after 429 responses.")
|
| 50 |
|
| 51 |
+
def _license_label_and_url(m: Dict[str, Any]) -> Tuple[str, Optional[str]]:
|
| 52 |
+
"""
|
| 53 |
+
Prefer explicit license_url; fall back to a 'license' text field if present.
|
| 54 |
+
Returns (label, url or None). Label is normalized toward SPDX-style.
|
| 55 |
+
"""
|
| 56 |
+
url = m.get("license_url")
|
| 57 |
+
txt = (m.get("license") or "").strip() if not url else ""
|
| 58 |
+
|
| 59 |
+
if url:
|
| 60 |
+
# Derive a short, user-friendly label from the URL path.
|
| 61 |
+
try:
|
| 62 |
+
import urllib.parse as up, re
|
| 63 |
+
last = up.urlparse(url).path.rstrip("/").split("/")[-1]
|
| 64 |
+
last = re.sub(r"(?i)\.(html|md)$", "", last)
|
| 65 |
+
# Normalize a few common SPDX forms
|
| 66 |
+
norm = {
|
| 67 |
+
"apache-2.0": "Apache-2.0",
|
| 68 |
+
"apache 2.0": "Apache-2.0",
|
| 69 |
+
"mit": "MIT",
|
| 70 |
+
"gpl-3.0": "GPL-3.0",
|
| 71 |
+
"gpl 3.0": "GPL-3.0",
|
| 72 |
+
"agpl-3.0": "AGPL-3.0",
|
| 73 |
+
"agpl 3.0": "AGPL-3.0",
|
| 74 |
+
"bsd-3-clause": "BSD-3-Clause",
|
| 75 |
+
"bsd 3 clause": "BSD-3-Clause",
|
| 76 |
+
"cc-by-4.0": "CC-BY-4.0",
|
| 77 |
+
"cc by 4.0": "CC-BY-4.0",
|
| 78 |
+
"cc-by-nc-4.0": "CC-BY-NC-4.0",
|
| 79 |
+
"cc by nc 4.0": "CC-BY-NC-4.0",
|
| 80 |
+
}
|
| 81 |
+
key = last.replace("_", "-").lower()
|
| 82 |
+
label = norm.get(key, (last or "License"))
|
| 83 |
+
except Exception:
|
| 84 |
+
label = "License"
|
| 85 |
+
return (label, url)
|
| 86 |
+
elif txt:
|
| 87 |
+
return (txt, None)
|
| 88 |
+
else:
|
| 89 |
+
return ("Unknown", None)
|
| 90 |
+
|
| 91 |
def _ensure_page_loaded(target_index: int) -> None:
|
| 92 |
"""
|
| 93 |
Ensure pages up to target_index (0-based) are loaded into _pages.
|
|
|
|
| 145 |
tags = m.get("tags") or m.get("categories") or []
|
| 146 |
tags_html = " ".join(f"<span class='tag'>{html.escape(str(t))}</span>" for t in tags[:8])
|
| 147 |
|
| 148 |
+
# License
|
| 149 |
+
lic_label, lic_url = _license_label_and_url(m)
|
| 150 |
+
lic_label_h = html.escape(lic_label)
|
| 151 |
+
lic_html = f"<a href='{html.escape(lic_url)}' target='_blank' rel='noopener'>{lic_label_h}</a>" if lic_url else lic_label_h
|
| 152 |
+
|
| 153 |
+
# External sources
|
| 154 |
+
src_links = []
|
| 155 |
+
if m.get("github_url"):
|
| 156 |
+
src_links.append(f"<a href='{html.escape(m['github_url'])}' target='_blank' rel='noopener'>GitHub</a>")
|
| 157 |
+
if m.get("paper_url"):
|
| 158 |
+
src_links.append(f"<a href='{html.escape(m['paper_url'])}' target='_blank' rel='noopener'>Paper</a>")
|
| 159 |
+
if lic_url:
|
| 160 |
+
src_links.append(f"<a href='{html.escape(lic_url)}' target='_blank' rel='noopener'>License</a>")
|
| 161 |
+
sources_html = " | ".join(src_links) if src_links else ""
|
| 162 |
+
|
| 163 |
+
# Run count
|
| 164 |
+
run_ct = m.get("run_count")
|
| 165 |
+
run_html = f"<span class='pill'>{run_ct:,} runs</span>" if isinstance(run_ct, int) else ""
|
| 166 |
+
|
| 167 |
thumb_html = f"<img src='{html.escape(thumb)}' alt='' />" if thumb else ""
|
| 168 |
rows.append(
|
| 169 |
f"<tr>"
|
| 170 |
f"<td class='thumb'>{thumb_html}</td>"
|
| 171 |
+
f"<td class='id'>{mid}<br>{run_html}</td>"
|
| 172 |
f"<td class='desc'>{desc}</td>"
|
| 173 |
f"<td class='vis'>{vis}</td>"
|
| 174 |
+
f"<td class='lic'>{lic_html}</td>"
|
| 175 |
+
f"<td class='src'>{sources_html}</td>"
|
| 176 |
f"<td class='tags'>{tags_html}</td>"
|
| 177 |
f"</tr>"
|
| 178 |
)
|
| 179 |
return (
|
| 180 |
"<table class='grid'>"
|
| 181 |
+
"<thead><tr>"
|
| 182 |
+
"<th>Thumbnail</th>"
|
| 183 |
+
"<th>ID / Runs</th>"
|
| 184 |
+
"<th>Description</th>"
|
| 185 |
+
"<th>Visibility</th>"
|
| 186 |
+
"<th>License</th>"
|
| 187 |
+
"<th>Sources</th>"
|
| 188 |
+
"<th>Tags</th>"
|
| 189 |
+
"</tr></thead>"
|
| 190 |
"<tbody>"
|
| 191 |
+ "\n".join(rows) +
|
| 192 |
"</tbody></table>"
|
|
|
|
| 255 |
.id {{ font-weight: 600; white-space: nowrap; }}
|
| 256 |
.desc {{ color: #333; max-width: 700px; }}
|
| 257 |
.vis {{ white-space: nowrap; color: #555; }}
|
| 258 |
+
.lic {{ white-space: nowrap; }}
|
| 259 |
+
.src {{ font-size: 13px; color: #0366d6; }}
|
| 260 |
.tags .tag {{ display: inline-block; padding: 2px 8px; margin: 2px; background: #f2f2f7; border: 1px solid #e5e5ea; border-radius: 999px; font-size: 12px; color: #333; }}
|
| 261 |
.pagination {{ margin-top: 20px; }}
|
| 262 |
.page {{ display: inline-block; padding: 6px 10px; margin-right: 6px; border: 1px solid #ddd; border-radius: 6px; text-decoration: none; color: #333; }}
|
|
|
|
| 277 |
<span class="pill">building: {building}</span>
|
| 278 |
<span class="pill">complete: {complete}</span>
|
| 279 |
</div>
|
|
|
|
| 280 |
<form class="searchbar" method="GET" action="/search" onsubmit="return onSearchSubmit();" >
|
| 281 |
<input id="q" name="q" type="text" placeholder="Search models, descriptions, tags" value="{html.escape(search_query)}" {search_disabled} />
|
| 282 |
<button id="qbtn" type="submit" {search_disabled}>Search</button>
|
| 283 |
<span id="searchHint" style="color:#666; font-size:12px;">{'' if _BUILD_COMPLETE else 'Search available after catalog completes.'}</span>
|
| 284 |
</form>
|
|
|
|
| 285 |
{html_table}
|
| 286 |
{_render_pagination(page_num)}
|
|
|
|
| 287 |
<script>
|
| 288 |
// Start building after the first render completes
|
| 289 |
document.addEventListener("DOMContentLoaded", function() {{
|
|
|
|
| 311 |
}};
|
| 312 |
poll();
|
| 313 |
}});
|
|
|
|
| 314 |
function onSearchSubmit() {{
|
| 315 |
const q = document.getElementById("q");
|
| 316 |
return q && !q.disabled;
|
|
|
|
| 379 |
base_models = _pages[idx]["results"] if _pages else []
|
| 380 |
if _BUILD_COMPLETE and q.strip():
|
| 381 |
results = _search_models(q)
|
| 382 |
+
content = _render_table(results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|