Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
"""
|
| 2 |
-
Space Keeper
|
| 3 |
"""
|
| 4 |
|
| 5 |
import os
|
|
@@ -84,7 +84,6 @@ def ping_space(space_id: str) -> dict:
|
|
| 84 |
"error": f"Timed out after {REQUEST_TIMEOUT}s"
|
| 85 |
}
|
| 86 |
except Exception as e:
|
| 87 |
-
# Fallback to HF page
|
| 88 |
try:
|
| 89 |
response = requests.get(hf_page_url, timeout=REQUEST_TIMEOUT)
|
| 90 |
return {
|
|
@@ -104,19 +103,6 @@ def ping_space(space_id: str) -> dict:
|
|
| 104 |
}
|
| 105 |
|
| 106 |
|
| 107 |
-
def get_org_space_count() -> int:
|
| 108 |
-
"""Try to get total space count from org page (includes private)."""
|
| 109 |
-
try:
|
| 110 |
-
url = f"https://huggingface.co/api/organizations/{ORG_NAME}/overview"
|
| 111 |
-
response = requests.get(url, timeout=10)
|
| 112 |
-
if response.status_code == 200:
|
| 113 |
-
data = response.json()
|
| 114 |
-
return data.get("numSpaces", -1)
|
| 115 |
-
except Exception:
|
| 116 |
-
pass
|
| 117 |
-
return -1
|
| 118 |
-
|
| 119 |
-
|
| 120 |
def scheduled_job():
|
| 121 |
"""Wrapper for the scheduled job."""
|
| 122 |
print(f"[{datetime.now(timezone.utc).isoformat()}] Running scheduled ping job...")
|
|
@@ -132,10 +118,10 @@ def scheduled_job():
|
|
| 132 |
"status": "error",
|
| 133 |
"error": f"Failed to list Spaces: {str(e)}",
|
| 134 |
"total_spaces": 0,
|
| 135 |
-
"spaces_found_by_api": 0,
|
| 136 |
"successful": 0,
|
| 137 |
"failed": 0,
|
| 138 |
"duration_seconds": 0,
|
|
|
|
| 139 |
"results": []
|
| 140 |
}
|
| 141 |
logs = load_logs()
|
|
@@ -143,6 +129,8 @@ def scheduled_job():
|
|
| 143 |
save_logs(logs)
|
| 144 |
return
|
| 145 |
|
|
|
|
|
|
|
| 146 |
space_ids = [space.id for space in spaces]
|
| 147 |
results = []
|
| 148 |
|
|
@@ -163,10 +151,10 @@ def scheduled_job():
|
|
| 163 |
"status": "completed",
|
| 164 |
"error": None,
|
| 165 |
"total_spaces": len(results),
|
| 166 |
-
"spaces_found_by_api": len(space_ids),
|
| 167 |
"successful": successful,
|
| 168 |
"failed": failed,
|
| 169 |
"duration_seconds": round(duration, 2),
|
|
|
|
| 170 |
"results": results
|
| 171 |
}
|
| 172 |
|
|
@@ -254,9 +242,12 @@ def format_logs_panel() -> str:
|
|
| 254 |
output += f"β **{ts}** | {by} | Error: {run['error']}\n\n"
|
| 255 |
else:
|
| 256 |
emoji = "β
" if run["failed"] == 0 else "β οΈ"
|
|
|
|
|
|
|
|
|
|
| 257 |
output += f"{emoji} **{ts}** | {by} | "
|
| 258 |
output += f"**{run['total_spaces']}** spaces | "
|
| 259 |
-
output += f"{run['successful']} ok, {run['failed']} failed | "
|
| 260 |
output += f"{run['duration_seconds']}s\n"
|
| 261 |
|
| 262 |
if run["failed"] > 0:
|
|
@@ -270,13 +261,32 @@ def format_logs_panel() -> str:
|
|
| 270 |
return output
|
| 271 |
|
| 272 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
# =============================================================================
|
| 274 |
# MAIN RUN FUNCTION WITH LIVE PROGRESS
|
| 275 |
# =============================================================================
|
| 276 |
def manual_trigger_with_progress():
|
| 277 |
"""Manually trigger a ping run with live progress updates."""
|
| 278 |
|
| 279 |
-
yield
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
start_time = datetime.now(timezone.utc)
|
| 282 |
|
|
@@ -291,37 +301,37 @@ def manual_trigger_with_progress():
|
|
| 291 |
"status": "error",
|
| 292 |
"error": str(e),
|
| 293 |
"total_spaces": 0,
|
| 294 |
-
"spaces_found_by_api": 0,
|
| 295 |
"successful": 0,
|
| 296 |
"failed": 0,
|
| 297 |
"duration_seconds": 0,
|
|
|
|
| 298 |
"results": []
|
| 299 |
}
|
| 300 |
logs = load_logs()
|
| 301 |
logs.append(run_result)
|
| 302 |
save_logs(logs)
|
| 303 |
-
yield error_msg, format_status_panel(), format_logs_panel()
|
| 304 |
return
|
| 305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
space_ids = [space.id for space in spaces]
|
| 307 |
total = len(space_ids)
|
| 308 |
|
| 309 |
-
# Try to get actual org space count for comparison
|
| 310 |
-
org_total = get_org_space_count()
|
| 311 |
-
|
| 312 |
-
# Show initial info
|
| 313 |
info_msg = f"## π Running...\n\n"
|
| 314 |
-
info_msg += f"**
|
| 315 |
-
if org_total > 0 and org_total != total:
|
| 316 |
-
diff = org_total - total
|
| 317 |
-
info_msg += f" (org has {org_total} total β {diff} are likely private)\n\n"
|
| 318 |
-
info_msg += f"π‘ *To ping private Spaces, add `HF_TOKEN` in Space settings*\n\n"
|
| 319 |
-
else:
|
| 320 |
-
info_msg += "\n\n"
|
| 321 |
info_msg += f"Pinging {PARALLEL_REQUESTS} Spaces in parallel...\n\n"
|
| 322 |
info_msg += f"`[0/{total}]` ββββββββββ 0%"
|
| 323 |
|
| 324 |
-
yield info_msg, format_status_panel(), format_logs_panel()
|
| 325 |
|
| 326 |
# Run pings
|
| 327 |
results = []
|
|
@@ -342,7 +352,6 @@ def manual_trigger_with_progress():
|
|
| 342 |
else:
|
| 343 |
failed += 1
|
| 344 |
|
| 345 |
-
# Progress bar
|
| 346 |
pct = int((completed / total) * 100)
|
| 347 |
filled = int(pct / 10)
|
| 348 |
bar = "β" * filled + "β" * (10 - filled)
|
|
@@ -360,7 +369,7 @@ def manual_trigger_with_progress():
|
|
| 360 |
"""
|
| 361 |
|
| 362 |
if completed % 5 == 0 or completed == total:
|
| 363 |
-
yield live_status, format_status_panel(), format_logs_panel()
|
| 364 |
|
| 365 |
# Save results
|
| 366 |
end_time = datetime.now(timezone.utc)
|
|
@@ -372,11 +381,10 @@ def manual_trigger_with_progress():
|
|
| 372 |
"status": "completed",
|
| 373 |
"error": None,
|
| 374 |
"total_spaces": total,
|
| 375 |
-
"spaces_found_by_api": total,
|
| 376 |
-
"org_total_spaces": org_total if org_total > 0 else None,
|
| 377 |
"successful": successful,
|
| 378 |
"failed": failed,
|
| 379 |
"duration_seconds": round(duration, 2),
|
|
|
|
| 380 |
"results": results
|
| 381 |
}
|
| 382 |
|
|
@@ -388,7 +396,7 @@ def manual_trigger_with_progress():
|
|
| 388 |
if failed == 0:
|
| 389 |
final = f"""## β
Completed Successfully!
|
| 390 |
|
| 391 |
-
**Pinged {total} Spaces** in {duration:.1f}
|
| 392 |
|
| 393 |
All Spaces responded! π
|
| 394 |
"""
|
|
@@ -400,7 +408,7 @@ All Spaces responded! π
|
|
| 400 |
|
| 401 |
final = f"""## β οΈ Completed with {failed} issue(s)
|
| 402 |
|
| 403 |
-
**Pinged {total} Spaces** in {duration:.1f}
|
| 404 |
|
| 405 |
β
{successful} successful
|
| 406 |
β {failed} failed
|
|
@@ -412,15 +420,16 @@ All Spaces responded! π
|
|
| 412 |
*Failures usually mean the Space is paused, has an error, or uses a non-standard SDK.*
|
| 413 |
"""
|
| 414 |
|
| 415 |
-
|
| 416 |
-
if org_total > 0 and org_total > total:
|
| 417 |
-
final += f"\n\n---\nπ‘ **Note:** {org_total - total} private Spaces were not pinged. Add `HF_TOKEN` to access them."
|
| 418 |
-
|
| 419 |
-
yield final, format_status_panel(), format_logs_panel()
|
| 420 |
|
| 421 |
|
| 422 |
def refresh_all():
|
| 423 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
|
| 425 |
|
| 426 |
# =============================================================================
|
|
@@ -442,10 +451,20 @@ Keeps HuggingFace Spaces alive by pinging them to prevent sleeping during hackat
|
|
| 442 |
live_output = gr.Markdown("## Ready\n\nClick **π Run Now** to ping all Spaces.\n\nAuto-runs every 12 hours when this Space is awake.")
|
| 443 |
|
| 444 |
gr.Markdown("---")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
logs_panel = gr.Markdown(format_logs_panel())
|
| 446 |
|
| 447 |
-
run_btn.click(
|
| 448 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 449 |
|
| 450 |
|
| 451 |
# =============================================================================
|
|
|
|
| 1 |
"""
|
| 2 |
+
Space Keeper v4 - Keeps HuggingFace Spaces alive during hackathon evaluation
|
| 3 |
"""
|
| 4 |
|
| 5 |
import os
|
|
|
|
| 84 |
"error": f"Timed out after {REQUEST_TIMEOUT}s"
|
| 85 |
}
|
| 86 |
except Exception as e:
|
|
|
|
| 87 |
try:
|
| 88 |
response = requests.get(hf_page_url, timeout=REQUEST_TIMEOUT)
|
| 89 |
return {
|
|
|
|
| 103 |
}
|
| 104 |
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
def scheduled_job():
|
| 107 |
"""Wrapper for the scheduled job."""
|
| 108 |
print(f"[{datetime.now(timezone.utc).isoformat()}] Running scheduled ping job...")
|
|
|
|
| 118 |
"status": "error",
|
| 119 |
"error": f"Failed to list Spaces: {str(e)}",
|
| 120 |
"total_spaces": 0,
|
|
|
|
| 121 |
"successful": 0,
|
| 122 |
"failed": 0,
|
| 123 |
"duration_seconds": 0,
|
| 124 |
+
"private_spaces": [],
|
| 125 |
"results": []
|
| 126 |
}
|
| 127 |
logs = load_logs()
|
|
|
|
| 129 |
save_logs(logs)
|
| 130 |
return
|
| 131 |
|
| 132 |
+
# Separate private and public spaces
|
| 133 |
+
private_space_names = [s.id.split("/")[1] for s in spaces if getattr(s, 'private', False)]
|
| 134 |
space_ids = [space.id for space in spaces]
|
| 135 |
results = []
|
| 136 |
|
|
|
|
| 151 |
"status": "completed",
|
| 152 |
"error": None,
|
| 153 |
"total_spaces": len(results),
|
|
|
|
| 154 |
"successful": successful,
|
| 155 |
"failed": failed,
|
| 156 |
"duration_seconds": round(duration, 2),
|
| 157 |
+
"private_spaces": private_space_names,
|
| 158 |
"results": results
|
| 159 |
}
|
| 160 |
|
|
|
|
| 242 |
output += f"β **{ts}** | {by} | Error: {run['error']}\n\n"
|
| 243 |
else:
|
| 244 |
emoji = "β
" if run["failed"] == 0 else "β οΈ"
|
| 245 |
+
private_count = len(run.get("private_spaces", []))
|
| 246 |
+
private_info = f" | π {private_count} private" if private_count > 0 else ""
|
| 247 |
+
|
| 248 |
output += f"{emoji} **{ts}** | {by} | "
|
| 249 |
output += f"**{run['total_spaces']}** spaces | "
|
| 250 |
+
output += f"{run['successful']} ok, {run['failed']} failed{private_info} | "
|
| 251 |
output += f"{run['duration_seconds']}s\n"
|
| 252 |
|
| 253 |
if run["failed"] > 0:
|
|
|
|
| 261 |
return output
|
| 262 |
|
| 263 |
|
| 264 |
+
def format_private_spaces_accordion(private_spaces: list) -> str:
|
| 265 |
+
"""Format the private spaces list for the accordion."""
|
| 266 |
+
if not private_spaces:
|
| 267 |
+
return "*No private Spaces found in this run.*"
|
| 268 |
+
|
| 269 |
+
output = f"**{len(private_spaces)} private Spaces** were pinged:\n\n"
|
| 270 |
+
|
| 271 |
+
# Create columns for better readability
|
| 272 |
+
for i, name in enumerate(sorted(private_spaces)):
|
| 273 |
+
output += f"- `{name}`\n"
|
| 274 |
+
|
| 275 |
+
return output
|
| 276 |
+
|
| 277 |
+
|
| 278 |
# =============================================================================
|
| 279 |
# MAIN RUN FUNCTION WITH LIVE PROGRESS
|
| 280 |
# =============================================================================
|
| 281 |
def manual_trigger_with_progress():
|
| 282 |
"""Manually trigger a ping run with live progress updates."""
|
| 283 |
|
| 284 |
+
yield (
|
| 285 |
+
"## π Starting...\n\nFetching Space list from HuggingFace API...",
|
| 286 |
+
format_status_panel(),
|
| 287 |
+
format_logs_panel(),
|
| 288 |
+
"*Run in progress...*"
|
| 289 |
+
)
|
| 290 |
|
| 291 |
start_time = datetime.now(timezone.utc)
|
| 292 |
|
|
|
|
| 301 |
"status": "error",
|
| 302 |
"error": str(e),
|
| 303 |
"total_spaces": 0,
|
|
|
|
| 304 |
"successful": 0,
|
| 305 |
"failed": 0,
|
| 306 |
"duration_seconds": 0,
|
| 307 |
+
"private_spaces": [],
|
| 308 |
"results": []
|
| 309 |
}
|
| 310 |
logs = load_logs()
|
| 311 |
logs.append(run_result)
|
| 312 |
save_logs(logs)
|
| 313 |
+
yield error_msg, format_status_panel(), format_logs_panel(), "*Error during run*"
|
| 314 |
return
|
| 315 |
|
| 316 |
+
# Separate private and public spaces
|
| 317 |
+
private_spaces = []
|
| 318 |
+
public_spaces = []
|
| 319 |
+
for s in spaces:
|
| 320 |
+
name = s.id.split("/")[1]
|
| 321 |
+
if getattr(s, 'private', False):
|
| 322 |
+
private_spaces.append(name)
|
| 323 |
+
else:
|
| 324 |
+
public_spaces.append(name)
|
| 325 |
+
|
| 326 |
space_ids = [space.id for space in spaces]
|
| 327 |
total = len(space_ids)
|
| 328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 329 |
info_msg = f"## π Running...\n\n"
|
| 330 |
+
info_msg += f"**Found {total} Spaces** ({len(public_spaces)} public, {len(private_spaces)} private)\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
info_msg += f"Pinging {PARALLEL_REQUESTS} Spaces in parallel...\n\n"
|
| 332 |
info_msg += f"`[0/{total}]` ββββββββββ 0%"
|
| 333 |
|
| 334 |
+
yield info_msg, format_status_panel(), format_logs_panel(), format_private_spaces_accordion(private_spaces)
|
| 335 |
|
| 336 |
# Run pings
|
| 337 |
results = []
|
|
|
|
| 352 |
else:
|
| 353 |
failed += 1
|
| 354 |
|
|
|
|
| 355 |
pct = int((completed / total) * 100)
|
| 356 |
filled = int(pct / 10)
|
| 357 |
bar = "β" * filled + "β" * (10 - filled)
|
|
|
|
| 369 |
"""
|
| 370 |
|
| 371 |
if completed % 5 == 0 or completed == total:
|
| 372 |
+
yield live_status, format_status_panel(), format_logs_panel(), format_private_spaces_accordion(private_spaces)
|
| 373 |
|
| 374 |
# Save results
|
| 375 |
end_time = datetime.now(timezone.utc)
|
|
|
|
| 381 |
"status": "completed",
|
| 382 |
"error": None,
|
| 383 |
"total_spaces": total,
|
|
|
|
|
|
|
| 384 |
"successful": successful,
|
| 385 |
"failed": failed,
|
| 386 |
"duration_seconds": round(duration, 2),
|
| 387 |
+
"private_spaces": private_spaces,
|
| 388 |
"results": results
|
| 389 |
}
|
| 390 |
|
|
|
|
| 396 |
if failed == 0:
|
| 397 |
final = f"""## β
Completed Successfully!
|
| 398 |
|
| 399 |
+
**Pinged {total} Spaces** ({len(public_spaces)} public, {len(private_spaces)} private) in {duration:.1f}s
|
| 400 |
|
| 401 |
All Spaces responded! π
|
| 402 |
"""
|
|
|
|
| 408 |
|
| 409 |
final = f"""## β οΈ Completed with {failed} issue(s)
|
| 410 |
|
| 411 |
+
**Pinged {total} Spaces** ({len(public_spaces)} public, {len(private_spaces)} private) in {duration:.1f}s
|
| 412 |
|
| 413 |
β
{successful} successful
|
| 414 |
β {failed} failed
|
|
|
|
| 420 |
*Failures usually mean the Space is paused, has an error, or uses a non-standard SDK.*
|
| 421 |
"""
|
| 422 |
|
| 423 |
+
yield final, format_status_panel(), format_logs_panel(), format_private_spaces_accordion(private_spaces)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 424 |
|
| 425 |
|
| 426 |
def refresh_all():
|
| 427 |
+
# Get private spaces from last run if available
|
| 428 |
+
logs = load_logs()
|
| 429 |
+
private_spaces = []
|
| 430 |
+
if logs:
|
| 431 |
+
private_spaces = logs[-1].get("private_spaces", [])
|
| 432 |
+
return format_status_panel(), format_logs_panel(), format_private_spaces_accordion(private_spaces)
|
| 433 |
|
| 434 |
|
| 435 |
# =============================================================================
|
|
|
|
| 451 |
live_output = gr.Markdown("## Ready\n\nClick **π Run Now** to ping all Spaces.\n\nAuto-runs every 12 hours when this Space is awake.")
|
| 452 |
|
| 453 |
gr.Markdown("---")
|
| 454 |
+
|
| 455 |
+
with gr.Accordion("π Private Spaces", open=False):
|
| 456 |
+
private_spaces_display = gr.Markdown("*Run a ping to see private Spaces list.*")
|
| 457 |
+
|
| 458 |
logs_panel = gr.Markdown(format_logs_panel())
|
| 459 |
|
| 460 |
+
run_btn.click(
|
| 461 |
+
fn=manual_trigger_with_progress,
|
| 462 |
+
outputs=[live_output, status_panel, logs_panel, private_spaces_display]
|
| 463 |
+
)
|
| 464 |
+
refresh_btn.click(
|
| 465 |
+
fn=refresh_all,
|
| 466 |
+
outputs=[status_panel, logs_panel, private_spaces_display]
|
| 467 |
+
)
|
| 468 |
|
| 469 |
|
| 470 |
# =============================================================================
|