Update app.py
Browse files
app.py
CHANGED
|
@@ -106,7 +106,14 @@ warmup_state: Dict[str, Any] = {
|
|
| 106 |
"total": 0,
|
| 107 |
"idx": 0,
|
| 108 |
"job_id": "default",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
}
|
|
|
|
| 110 |
warmup_lock = threading.Lock()
|
| 111 |
warmup_stop = threading.Event()
|
| 112 |
|
|
@@ -187,25 +194,13 @@ def _download_one(repo_id: str, tries: int = 3) -> bool:
|
|
| 187 |
return False
|
| 188 |
|
| 189 |
def _warmup_thread(models: List[str]):
|
| 190 |
-
with warmup_lock:
|
| 191 |
-
warmup_state.update({
|
| 192 |
-
"running": True,
|
| 193 |
-
"percent": 0,
|
| 194 |
-
"ok_count": 0,
|
| 195 |
-
"done": False,
|
| 196 |
-
"logs": [],
|
| 197 |
-
"current": None,
|
| 198 |
-
"total": len(models),
|
| 199 |
-
"idx": 0,
|
| 200 |
-
})
|
| 201 |
-
warmup_stop.clear()
|
| 202 |
ok_count = 0
|
| 203 |
t_global = time.time()
|
| 204 |
_log_warmup(f"[JOB] start • {len(models)} dépôts")
|
| 205 |
|
| 206 |
for i, repo in enumerate(models):
|
| 207 |
if warmup_stop.is_set():
|
| 208 |
-
_log_warmup("[STOP] Arrêt demandé
|
| 209 |
break
|
| 210 |
|
| 211 |
with warmup_lock:
|
|
@@ -214,12 +209,14 @@ def _warmup_thread(models: List[str]):
|
|
| 214 |
warmup_state["percent"] = int((i / max(1, len(models))) * 100)
|
| 215 |
|
| 216 |
ok = _download_one(repo)
|
| 217 |
-
if ok:
|
| 218 |
-
ok_count += 1
|
| 219 |
-
with warmup_lock:
|
| 220 |
-
warmup_state["ok_count"] = ok_count
|
| 221 |
-
# progression après dépôt (OK ou FAIL)
|
| 222 |
with warmup_lock:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
warmup_state["percent"] = int(((i + 1) / max(1, len(models))) * 100)
|
| 224 |
|
| 225 |
if warmup_stop.is_set():
|
|
@@ -231,8 +228,10 @@ def _warmup_thread(models: List[str]):
|
|
| 231 |
warmup_state["done"] = True
|
| 232 |
warmup_state["running"] = False
|
| 233 |
warmup_state["ok_count"] = ok_count
|
|
|
|
| 234 |
_log_warmup(f"[JOB] done • {ok_count}/{len(models)} • {time.time()-t_global:.1f}s")
|
| 235 |
|
|
|
|
| 236 |
# <<<< HUGINFACE PATCH: WARMUP STATE+HELPERS END >>>>
|
| 237 |
# ---------- Helpers ----------
|
| 238 |
|
|
@@ -519,7 +518,7 @@ from copy import deepcopy
|
|
| 519 |
|
| 520 |
@app.post("/warmup/start", tags=["warmup"])
|
| 521 |
async def warmup_start(payload: Optional[Dict[str, Any]] = Body(None)):
|
| 522 |
-
|
| 523 |
Démarre le téléchargement séquentiel des modèles.
|
| 524 |
Corps JSON optionnel: { "models": ["repo1","repo2", ...] }
|
| 525 |
Si vide: lit la variable d'env WARMUP_MODELS (JSON).
|
|
@@ -538,20 +537,31 @@ async def warmup_start(payload: Optional[Dict[str, Any]] = Body(None)):
|
|
| 538 |
|
| 539 |
with warmup_lock:
|
| 540 |
if warmup_state.get("running"):
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
"ok": False,
|
| 544 |
-
"already_running": True,
|
| 545 |
-
"status": deepcopy(warmup_state),
|
| 546 |
-
}
|
| 547 |
-
# Lancer un nouveau thread
|
| 548 |
job_id = uuid.uuid4().hex[:8]
|
| 549 |
-
warmup_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
th = threading.Thread(target=_warmup_thread, args=(models,), daemon=True)
|
| 551 |
th.start()
|
| 552 |
|
| 553 |
return {"ok": True, "started": True, "total": len(models), "job_id": job_id}
|
| 554 |
|
|
|
|
| 555 |
@app.get("/warmup/status", tags=["warmup"])
|
| 556 |
def warmup_status():
|
| 557 |
"""Retourne l'état courant du warm-up (progression, logs, etc.)."""
|
|
@@ -835,12 +845,12 @@ HTML_TEMPLATE = r"""
|
|
| 835 |
|
| 836 |
<!-- >>> A2B2_BEGIN warmup_topbar -->
|
| 837 |
<div style="display:flex;gap:8px;align-items:center">
|
| 838 |
-
|
| 839 |
-
<button id="warmupStopBtn" class="btn" title="Arrêter le
|
| 840 |
-
<button id="warmupSelectBtn" class="btn" title="Sélectionner les repos à
|
| 841 |
-
|
| 842 |
-
<button id="warmupLogsBtn" class="btn" title="Voir les logs du warm-up">📜 Logs warm-up</button>
|
| 843 |
<span id="warmupStatus" class="muted"></span>
|
|
|
|
| 844 |
</div>
|
| 845 |
<!-- >>> A2B2_END warmup_topbar -->
|
| 846 |
|
|
@@ -920,7 +930,8 @@ HTML_TEMPLATE = r"""
|
|
| 920 |
|
| 921 |
<!-- >>> A2B2_BEGIN warmup_popup -->
|
| 922 |
<div id="warmupPopup" style="display:none; position:fixed; top:22%; left:50%; transform:translate(-50%,-50%); background:#fff; padding:20px; border-radius:8px; box-shadow:0 0 10px rgba(0,0,0,0.2); z-index:1001; min-width:360px">
|
| 923 |
-
|
|
|
|
| 924 |
<div class="muted" id="warmupPopupStatus">—</div>
|
| 925 |
<div style="background:#f3f4f6;border-radius:4px;height:8px;margin:10px 0">
|
| 926 |
<div id="warmup-progress-fill" style="background:#2563eb;height:100%;width:0;border-radius:4px"></div>
|
|
@@ -937,7 +948,8 @@ HTML_TEMPLATE = r"""
|
|
| 937 |
<button id="selectAllBtn" class="btn">Tout cocher</button>
|
| 938 |
<button id="selectNoneBtn" class="btn">Tout décocher</button>
|
| 939 |
<button id="selectCancelBtn" class="btn">Annuler</button>
|
| 940 |
-
|
|
|
|
| 941 |
</div>
|
| 942 |
</div>
|
| 943 |
<!-- >>> A2B2_END warmup_popup -->
|
|
@@ -1001,6 +1013,32 @@ window._lastInstanceId = null;
|
|
| 1001 |
const warmupCloseBtn = document.getElementById('warmupCloseBtn');
|
| 1002 |
|
| 1003 |
// Helpers popup
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1004 |
|
| 1005 |
|
| 1006 |
// >>> C2_BEGIN warmup_preface
|
|
@@ -1048,32 +1086,25 @@ async function refreshWarmupUI(){
|
|
| 1048 |
if (warmupStatusEl) {
|
| 1049 |
const tot = (s.total ?? 0);
|
| 1050 |
if (running) {
|
| 1051 |
-
|
| 1052 |
warmupStatusEl.textContent = `⏳ ${pct}% — ${s.current||''} (${idx}/${tot}) [inst:${instanceId}]`;
|
| 1053 |
} else {
|
| 1054 |
-
|
| 1055 |
-
|
| 1056 |
-
|
| 1057 |
-
|
| 1058 |
-
|
| 1059 |
-
|
| 1060 |
-
|
| 1061 |
-
|
| 1062 |
-
|
| 1063 |
-
|
| 1064 |
-
|
| 1065 |
-
|
| 1066 |
-
|
| 1067 |
-
|
| 1068 |
-
: (Array.isArray(s.audit_cached) ? s.audit_cached.length : 0);
|
| 1069 |
-
if (nCache > 0) {
|
| 1070 |
-
warmupStatusEl.textContent = `✅ Prêt — cache local: ${nCache} (inst:${instanceId})`;
|
| 1071 |
-
} else {
|
| 1072 |
-
warmupStatusEl.textContent = `Prêt (aucun run)`;
|
| 1073 |
}
|
| 1074 |
-
}
|
| 1075 |
|
| 1076 |
-
}
|
| 1077 |
}
|
| 1078 |
|
| 1079 |
if (warmupProgressFill) warmupProgressFill.style.width = pct + '%';
|
|
|
|
| 106 |
"total": 0,
|
| 107 |
"idx": 0,
|
| 108 |
"job_id": "default",
|
| 109 |
+
# NOUVEAUX CHAMPS POUR RÉCAP
|
| 110 |
+
"asked": [], # liste demandée au lancement
|
| 111 |
+
"ok_repos": [], # liste des repos téléchargés OK
|
| 112 |
+
"failed_repos": [], # liste des repos en échec
|
| 113 |
+
"started_at": 0.0, # timestamp début
|
| 114 |
+
"finished_at": 0.0, # timestamp fin
|
| 115 |
}
|
| 116 |
+
|
| 117 |
warmup_lock = threading.Lock()
|
| 118 |
warmup_stop = threading.Event()
|
| 119 |
|
|
|
|
| 194 |
return False
|
| 195 |
|
| 196 |
def _warmup_thread(models: List[str]):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
ok_count = 0
|
| 198 |
t_global = time.time()
|
| 199 |
_log_warmup(f"[JOB] start • {len(models)} dépôts")
|
| 200 |
|
| 201 |
for i, repo in enumerate(models):
|
| 202 |
if warmup_stop.is_set():
|
| 203 |
+
_log_warmup("[STOP] Arrêt demandé — fin du job après ce point")
|
| 204 |
break
|
| 205 |
|
| 206 |
with warmup_lock:
|
|
|
|
| 209 |
warmup_state["percent"] = int((i / max(1, len(models))) * 100)
|
| 210 |
|
| 211 |
ok = _download_one(repo)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
with warmup_lock:
|
| 213 |
+
if ok:
|
| 214 |
+
ok_count += 1
|
| 215 |
+
warmup_state["ok_count"] = ok_count
|
| 216 |
+
warmup_state["ok_repos"].append(repo)
|
| 217 |
+
else:
|
| 218 |
+
warmup_state["failed_repos"].append(repo)
|
| 219 |
+
|
| 220 |
warmup_state["percent"] = int(((i + 1) / max(1, len(models))) * 100)
|
| 221 |
|
| 222 |
if warmup_stop.is_set():
|
|
|
|
| 228 |
warmup_state["done"] = True
|
| 229 |
warmup_state["running"] = False
|
| 230 |
warmup_state["ok_count"] = ok_count
|
| 231 |
+
warmup_state["finished_at"] = time.time()
|
| 232 |
_log_warmup(f"[JOB] done • {ok_count}/{len(models)} • {time.time()-t_global:.1f}s")
|
| 233 |
|
| 234 |
+
|
| 235 |
# <<<< HUGINFACE PATCH: WARMUP STATE+HELPERS END >>>>
|
| 236 |
# ---------- Helpers ----------
|
| 237 |
|
|
|
|
| 518 |
|
| 519 |
@app.post("/warmup/start", tags=["warmup"])
|
| 520 |
async def warmup_start(payload: Optional[Dict[str, Any]] = Body(None)):
|
| 521 |
+
"""
|
| 522 |
Démarre le téléchargement séquentiel des modèles.
|
| 523 |
Corps JSON optionnel: { "models": ["repo1","repo2", ...] }
|
| 524 |
Si vide: lit la variable d'env WARMUP_MODELS (JSON).
|
|
|
|
| 537 |
|
| 538 |
with warmup_lock:
|
| 539 |
if warmup_state.get("running"):
|
| 540 |
+
return {"ok": False, "already_running": True, "status": deepcopy(warmup_state)}
|
| 541 |
+
# RESET d’un nouveau job
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 542 |
job_id = uuid.uuid4().hex[:8]
|
| 543 |
+
warmup_state.update({
|
| 544 |
+
"job_id": job_id,
|
| 545 |
+
"asked": models[:],
|
| 546 |
+
"ok_repos": [],
|
| 547 |
+
"failed_repos": [],
|
| 548 |
+
"ok_count": 0,
|
| 549 |
+
"logs": [],
|
| 550 |
+
"idx": 0,
|
| 551 |
+
"total": len(models),
|
| 552 |
+
"percent": 0,
|
| 553 |
+
"running": True,
|
| 554 |
+
"done": False,
|
| 555 |
+
"current": None,
|
| 556 |
+
"started_at": time.time(),
|
| 557 |
+
"finished_at": 0.0,
|
| 558 |
+
})
|
| 559 |
th = threading.Thread(target=_warmup_thread, args=(models,), daemon=True)
|
| 560 |
th.start()
|
| 561 |
|
| 562 |
return {"ok": True, "started": True, "total": len(models), "job_id": job_id}
|
| 563 |
|
| 564 |
+
|
| 565 |
@app.get("/warmup/status", tags=["warmup"])
|
| 566 |
def warmup_status():
|
| 567 |
"""Retourne l'état courant du warm-up (progression, logs, etc.)."""
|
|
|
|
| 845 |
|
| 846 |
<!-- >>> A2B2_BEGIN warmup_topbar -->
|
| 847 |
<div style="display:flex;gap:8px;align-items:center">
|
| 848 |
+
<button id="warmupStartBtn" class="btn" title="Télécharger les modèles (séquentiel)">🔥 Télécharger modèles</button>
|
| 849 |
+
<button id="warmupStopBtn" class="btn" title="Arrêter le téléchargement en cours" style="display:none">⏹️ Stop téléchargement</button>
|
| 850 |
+
<button id="warmupSelectBtn" class="btn" title="Sélectionner les repos à télécharger">🧩 Choisir modèles</button>
|
| 851 |
+
<button id="warmupLogsBtn" class="btn" title="Voir les logs du téléchargement">📜 Logs téléchargement</button>
|
|
|
|
| 852 |
<span id="warmupStatus" class="muted"></span>
|
| 853 |
+
|
| 854 |
</div>
|
| 855 |
<!-- >>> A2B2_END warmup_topbar -->
|
| 856 |
|
|
|
|
| 930 |
|
| 931 |
<!-- >>> A2B2_BEGIN warmup_popup -->
|
| 932 |
<div id="warmupPopup" style="display:none; position:fixed; top:22%; left:50%; transform:translate(-50%,-50%); background:#fff; padding:20px; border-radius:8px; box-shadow:0 0 10px rgba(0,0,0,0.2); z-index:1001; min-width:360px">
|
| 933 |
+
<h3>Téléchargement des modèles (warm-up)</h3>
|
| 934 |
+
|
| 935 |
<div class="muted" id="warmupPopupStatus">—</div>
|
| 936 |
<div style="background:#f3f4f6;border-radius:4px;height:8px;margin:10px 0">
|
| 937 |
<div id="warmup-progress-fill" style="background:#2563eb;height:100%;width:0;border-radius:4px"></div>
|
|
|
|
| 948 |
<button id="selectAllBtn" class="btn">Tout cocher</button>
|
| 949 |
<button id="selectNoneBtn" class="btn">Tout décocher</button>
|
| 950 |
<button id="selectCancelBtn" class="btn">Annuler</button>
|
| 951 |
+
<button id="launchSelectedBtn" class="btn">Lancer le téléchargement</button>
|
| 952 |
+
|
| 953 |
</div>
|
| 954 |
</div>
|
| 955 |
<!-- >>> A2B2_END warmup_popup -->
|
|
|
|
| 1013 |
const warmupCloseBtn = document.getElementById('warmupCloseBtn');
|
| 1014 |
|
| 1015 |
// Helpers popup
|
| 1016 |
+
function buildFinalSummary(s){
|
| 1017 |
+
const asked = Array.isArray(s.asked) ? s.asked : [];
|
| 1018 |
+
const okL = Array.isArray(s.ok_repos) ? s.ok_repos : [];
|
| 1019 |
+
const failL = Array.isArray(s.failed_repos) ? s.failed_repos : [];
|
| 1020 |
+
const cached = Array.isArray(s.audit_cached) ? s.audit_cached : [];
|
| 1021 |
+
const tot = s.total ?? asked.length ?? 0;
|
| 1022 |
+
|
| 1023 |
+
const lines = [];
|
| 1024 |
+
lines.push("RÉCAP FINAL");
|
| 1025 |
+
lines.push(`Demandés : ${asked.length}`);
|
| 1026 |
+
if (asked.length) lines.push(asked.map(m=>' • '+m).join('\n'));
|
| 1027 |
+
|
| 1028 |
+
lines.push("");
|
| 1029 |
+
lines.push(`✅ Téléchargés : ${okL.length}/${tot}`);
|
| 1030 |
+
if (okL.length) lines.push(okL.map(m=>' • '+m).join('\n'));
|
| 1031 |
+
|
| 1032 |
+
lines.push("");
|
| 1033 |
+
lines.push(`❌ Échecs : ${failL.length}`);
|
| 1034 |
+
if (failL.length) lines.push(failL.map(m=>' • '+m).join('\n'));
|
| 1035 |
+
|
| 1036 |
+
lines.push("");
|
| 1037 |
+
lines.push(`📦 En cache maintenant : ${cached.length}`);
|
| 1038 |
+
if (cached.length) lines.push(cached.map(m=>' • '+m).join('\n'));
|
| 1039 |
+
|
| 1040 |
+
return lines.join('\n');
|
| 1041 |
+
}
|
| 1042 |
|
| 1043 |
|
| 1044 |
// >>> C2_BEGIN warmup_preface
|
|
|
|
| 1086 |
if (warmupStatusEl) {
|
| 1087 |
const tot = (s.total ?? 0);
|
| 1088 |
if (running) {
|
| 1089 |
+
const idx = (s.idx ?? 0) + 1;
|
| 1090 |
warmupStatusEl.textContent = `⏳ ${pct}% — ${s.current||''} (${idx}/${tot}) [inst:${instanceId}]`;
|
| 1091 |
} else {
|
| 1092 |
+
if (s.done) {
|
| 1093 |
+
const ok = Number.isFinite(s.ok_count) ? s.ok_count : 0;
|
| 1094 |
+
const fail = Array.isArray(s.failed_repos) ? s.failed_repos.length : Math.max(0, tot - ok);
|
| 1095 |
+
warmupStatusEl.textContent = `✅ Terminé — ${ok}/${tot} téléchargés, ${fail} échec(s) [inst:${instanceId}]`;
|
| 1096 |
+
if (warmupPopupStatus) warmupPopupStatus.textContent = 'Terminé';
|
| 1097 |
+
if (warmupLogs) warmupLogs.textContent = buildFinalSummary(s);
|
| 1098 |
+
} else {
|
| 1099 |
+
const nCache = Number.isFinite(s.audit_count)
|
| 1100 |
+
? s.audit_count
|
| 1101 |
+
: (Array.isArray(s.audit_cached) ? s.audit_cached.length : 0);
|
| 1102 |
+
warmupStatusEl.textContent = nCache > 0
|
| 1103 |
+
? `✅ Prêt — cache local: ${nCache} [inst:${instanceId}]`
|
| 1104 |
+
: `Prêt (aucun run)`;
|
| 1105 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1106 |
}
|
|
|
|
| 1107 |
|
|
|
|
| 1108 |
}
|
| 1109 |
|
| 1110 |
if (warmupProgressFill) warmupProgressFill.style.width = pct + '%';
|