jean-router: detection a 3 niveaux - PC / Jean / Ollama

- PC: TCP connect (ON/OFF) → WoL uniquement si OFF
- Jean: HTTP /api/presets (UP/DOWN)
- Ollama: HTTP /api/version (UP/DOWN)
- Dashboard: 3 lignes de statut distinctes
- WoL ne se declenche que si PC OFF, pas si service DOWN
This commit is contained in:
Folzo
2026-08-02 01:19:35 +02:00
parent dc900172f2
commit e93ab95350
+48 -17
View File
@@ -146,16 +146,34 @@ def jean_alive() -> bool:
alive = True alive = True
break break
except ConnectionRefusedError: except ConnectionRefusedError:
alive = True # PC on, service down alive = True
break break
except (socket.timeout, OSError): except (socket.timeout, OSError):
continue # PC off ou inaccessible continue
jean_alive_cache["alive"] = alive jean_alive_cache["alive"] = alive
jean_alive_cache["timestamp"] = now jean_alive_cache["timestamp"] = now
return alive return alive
def jean_service_ok() -> bool:
"""Verifie que le service Jean repond (HTTP)"""
try:
r = requests.get(f"http://{JEAN_WEB_HOST}:{JEAN_WEB_PORT}/api/presets", timeout=3)
return r.ok
except Exception:
return False
def ollama_service_ok() -> bool:
"""Verifie que le service Ollama repond (HTTP)"""
try:
r = requests.get(f"http://{OLLAMA_DIRECT_HOST}:{OLLAMA_DIRECT_PORT}/api/version", timeout=2)
return r.ok
except Exception:
return False
def wait_for_jean(trigger_wol=True): def wait_for_jean(trigger_wol=True):
"""Attend que Jean soit accessible. Si ENABLE_WOL et trigger_wol, envoie WoL d'abord.""" """Attend que Jean soit accessible. Si ENABLE_WOL et trigger_wol, envoie WoL d'abord."""
alive = jean_alive() alive = jean_alive()
@@ -888,6 +906,9 @@ def chat_completions():
data, fwd_headers data, fwd_headers
) )
if not jean_service_ok():
log.warning(f"[Router] PC ON mais Jean DOWN, requete vers {model_id} risque d'echouer")
if not ensure_vram_for_backend("jean"): if not ensure_vram_for_backend("jean"):
return jsonify({"error": "Failed to prepare VRAM for Jean"}), 503 return jsonify({"error": "Failed to prepare VRAM for Jean"}), 503
@@ -994,13 +1015,9 @@ def dashboard():
@app.route("/api/dashboard/status") @app.route("/api/dashboard/status")
def dashboard_status(): def dashboard_status():
"""Etat complet en JSON""" """Etat complet en JSON"""
jean_ok = jean_alive() pc_alive = jean_alive()
olla_ok = False jean_ok = jean_service_ok() if pc_alive else False
try: olla_ok = ollama_service_ok() if pc_alive else False
r = requests.get(f"http://{OLLAMA_DIRECT_HOST}:{OLLAMA_DIRECT_PORT}/api/version", timeout=2)
olla_ok = r.ok
except Exception:
pass
try: try:
r = requests.get(f"http://{JEAN_WEB_HOST}:{JEAN_WEB_PORT}/api/presets", timeout=1) r = requests.get(f"http://{JEAN_WEB_HOST}:{JEAN_WEB_PORT}/api/presets", timeout=1)
@@ -1029,6 +1046,10 @@ def dashboard_status():
stats = dict(request_stats) stats = dict(request_stats)
return jsonify({ return jsonify({
"pc": {
"alive": pc_alive,
"host": JEAN_WEB_HOST,
},
"jean": { "jean": {
"alive": jean_ok, "alive": jean_ok,
"host": f"{JEAN_WEB_HOST}:{JEAN_WEB_PORT}", "host": f"{JEAN_WEB_HOST}:{JEAN_WEB_PORT}",
@@ -1134,7 +1155,7 @@ h1{font-size:18px;color:var(--purple);margin-bottom:12px}
.badge-blue{background:#1a2a3a;color:var(--blue);border:1px solid var(--blue)} .badge-blue{background:#1a2a3a;color:var(--blue);border:1px solid var(--blue)}
#logs{grid-column:1/-1;height:400px;overflow-y:auto;font-size:12px;line-height:1.6;border:1px solid var(--border);border-radius:6px;padding:10px;background:#0a0e13} #logs{grid-column:1/-1;height:400px;overflow-y:auto;font-size:12px;line-height:1.6;border:1px solid var(--border);border-radius:6px;padding:10px;background:#0a0e13}
#logs .error{color:var(--red)} #logs .error{color:var(--red)}
#logs .warn{color:var(--yellow)} #logs .warn{color:#f0883e;font-weight:500}
#logs .info{color:var(--muted)} #logs .info{color:var(--muted)}
#logs .debug{color:#484f58} #logs .debug{color:#484f58}
#logs .highlight{color:var(--green);font-weight:700} #logs .highlight{color:var(--green);font-weight:700}
@@ -1157,10 +1178,11 @@ h1{font-size:18px;color:var(--purple);margin-bottom:12px}
<div class="grid"> <div class="grid">
<div class="card"> <div class="card">
<h2>🔌 Statut</h2> <h2>🔌 Statut</h2>
<div class="row"><span class="label">Jean</span><span id="jean-status">-</span></div> <div class="row"><span class="label">PC Gaming</span><span id="pc-status">-</span></div>
<div class="row"><span class="label">Ollama</span><span id="ollama-status">-</span></div> <div class="row"><span class="label">Jean (8090)</span><span id="jean-status">-</span></div>
<div class="row"><span class="label">VRAM Backend</span><span id="vram-status">-</span></div> <div class="row"><span class="label">Ollama (11434)</span><span id="ollama-status">-</span></div>
<div class="row"><span class="label">Preset actif</span><span id="active-preset">-</span></div> <div class="row"><span class="label">VRAM</span><span id="vram-status">-</span></div>
<div class="row"><span class="label">Preset</span><span id="active-preset">-</span></div>
</div> </div>
<div class="card"> <div class="card">
<h2>🎰 Slot Jean</h2> <h2>🎰 Slot Jean</h2>
@@ -1211,9 +1233,18 @@ async function fetchStatus() {
const d = await r.json(); const d = await r.json();
// Status dots // Status dots
$('#jean-status').innerHTML = (d.jean.alive ? '<span class="status-dot on"></span>' : '<span class="status-dot off"></span>') + 'Jean : ' + d.jean.host; $('#pc-status').innerHTML = (d.pc.alive
$('#ollama-status').innerHTML = (d.ollama.alive ? '<span class="status-dot on"></span>' : '<span class="status-dot off"></span>') + 'Ollama : ' + d.ollama.host; ? '<span class="status-dot on"></span> ON'
$('#vram-status').innerHTML = (d.jean.vram_backend === 'jean' ? '<span class="badge badge-blue">JEAN</span>' : '<span class="badge badge-green">OLLAMA</span>'); : '<span class="status-dot off"></span> OFF — WoL requis');
$('#jean-status').innerHTML = (d.jean.alive
? '<span class="status-dot on"></span> UP'
: (d.pc.alive ? '<span class="status-dot warn"></span> DOWN (PC ON)' : '<span class="status-dot off"></span> DOWN'));
$('#ollama-status').innerHTML = (d.ollama.alive
? '<span class="status-dot on"></span> UP'
: (d.pc.alive ? '<span class="status-dot warn"></span> DOWN (PC ON)' : '<span class="status-dot off"></span> DOWN'));
$('#vram-status').innerHTML = (d.jean.vram_backend === 'jean'
? '<span class="badge badge-blue">JEAN</span>'
: '<span class="badge badge-green">OLLAMA</span>');
$('#active-preset').innerHTML = '<span class="badge badge-green">' + d.jean.active_preset + '</span>'; $('#active-preset').innerHTML = '<span class="badge badge-green">' + d.jean.active_preset + '</span>';
// Slot // Slot