jean-router: streaming de messages d'attente pendant le WoL
- Quand le PC est eteint/en veille, stream immediat de messages - 'PC de jeu se reveille...' → 'Magic packet envoye...' → 'Demarrage X%...' - Une fois pret, forward la vraie reponse en stream - Plus de blocage silencieux, l'utilisateur voit la progression
This commit is contained in:
+87
-3
@@ -13,7 +13,7 @@ import socket
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
from flask import Flask, request, Response, jsonify
|
||||
from flask import Flask, request, Response, jsonify, stream_with_context
|
||||
|
||||
JEAN_WEB_HOST = os.getenv("JEAN_WEB_HOST", "192.168.1.11")
|
||||
JEAN_WEB_PORT = int(os.getenv("JEAN_WEB_PORT", "8090"))
|
||||
@@ -34,7 +34,7 @@ OLLAMA_UNLOAD_TIMEOUT = int(os.getenv("OLLAMA_UNLOAD_TIMEOUT", "15"))
|
||||
ENABLE_WOL = os.getenv("ENABLE_WOL", "false").lower() == "true"
|
||||
TARGET_MAC = os.getenv("TARGET_MAC", os.getenv("PC_GAMING_MAC", ""))
|
||||
BROADCAST_IP = os.getenv("BROADCAST_IP", "192.168.1.255")
|
||||
WAKE_TIMEOUT = int(os.getenv("WAKE_TIMEOUT", "120"))
|
||||
WAKE_TIMEOUT = int(os.getenv("WAKE_TIMEOUT", "180"))
|
||||
wol_in_progress = threading.Lock()
|
||||
jean_alive_cache = {"alive": False, "timestamp": 0}
|
||||
|
||||
@@ -152,6 +152,83 @@ def wait_for_jean(trigger_wol=True):
|
||||
log.error(f"[WoL] Jean ne repond pas apres {WAKE_TIMEOUT}s")
|
||||
|
||||
|
||||
def stream_wake_then_forward(upstream_url, data, headers):
|
||||
"""Stream des messages d'attente pendant le reveil, puis forward la vraie reponse"""
|
||||
import uuid
|
||||
chat_id = f"chatcmpl-{uuid.uuid4().hex[:29]}"
|
||||
|
||||
def wake_chunk(content):
|
||||
return (
|
||||
f'data: {{"id":"{chat_id}","object":"chat.completion.chunk",'
|
||||
f'"created":{int(time.time())},"model":"wake","choices":['
|
||||
f'{{"delta":{{"content":"{content} "}},"index":0}}]}}\n\n'
|
||||
).encode()
|
||||
|
||||
def generate():
|
||||
yield wake_chunk("🖥️ Le PC de jeu se réveille...")
|
||||
|
||||
if not jean_alive() and ENABLE_WOL:
|
||||
with wol_in_progress:
|
||||
if not jean_alive():
|
||||
log.info("[WoL-Stream] Envoi WoL + attente...")
|
||||
send_wol()
|
||||
yield wake_chunk("⚡ Magic packet envoyé, démarrage en cours...")
|
||||
|
||||
deadline = time.time() + WAKE_TIMEOUT
|
||||
last_pct = 0
|
||||
while time.time() < deadline:
|
||||
if jean_alive():
|
||||
jean_alive_cache["timestamp"] = 0
|
||||
yield wake_chunk("✅ PC allumé, chargement du modèle...")
|
||||
break
|
||||
pct = int((1 - (deadline - time.time()) / WAKE_TIMEOUT) * 100)
|
||||
if pct >= last_pct + 10:
|
||||
yield wake_chunk(f"⏳ Démarrage... {min(pct, 95)}%")
|
||||
last_pct = pct
|
||||
time.sleep(CHECK_INTERVAL)
|
||||
else:
|
||||
yield wake_chunk("❌ Le PC n'a pas répondu, réessayez.")
|
||||
yield b"data: [DONE]\n\n"
|
||||
return
|
||||
|
||||
# Attendre que Jean soit vraiment prêt (proxy + modele)
|
||||
deadline = time.time() + SWITCH_TIMEOUT
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
r = requests.get(f"http://{JEAN_PROXY_HOST}:{JEAN_PROXY_PORT}/health", timeout=2)
|
||||
if r.ok and r.json().get("status") == "ok":
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
yield wake_chunk(".")
|
||||
time.sleep(2)
|
||||
|
||||
yield wake_chunk("🚀 Modèle prêt, voici la réponse :\n\n")
|
||||
|
||||
# Forward la vraie requête et streamer
|
||||
try:
|
||||
resp = requests.request(
|
||||
method="POST", url=upstream_url, headers=headers,
|
||||
json=data, stream=True, timeout=None,
|
||||
)
|
||||
for line in resp.iter_lines():
|
||||
if line:
|
||||
yield line + b"\n"
|
||||
yield b"data: [DONE]\n\n"
|
||||
except Exception as e:
|
||||
yield wake_chunk(f"❌ Erreur: {str(e)[:100]}")
|
||||
yield b"data: [DONE]\n\n"
|
||||
|
||||
return Response(
|
||||
stream_with_context(generate()),
|
||||
content_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Accel-Buffering": "no",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def get_ollama_models(force_refresh=False):
|
||||
"""Récupère la liste des modèles depuis Ollama"""
|
||||
if not ENABLE_OLLAMA:
|
||||
@@ -761,7 +838,14 @@ def chat_completions():
|
||||
if model_id in jean_preset_ids or model_id in jean_names:
|
||||
internal_id = jean_name_to_id.get(model_id, model_id)
|
||||
|
||||
wait_for_jean(trigger_wol=True)
|
||||
if not jean_alive() and ENABLE_WOL:
|
||||
log.info("[WoL-Stream] PC down, demarrage stream d'attente...")
|
||||
fwd_headers = dict(request.headers)
|
||||
fwd_headers.pop("Host", None)
|
||||
return stream_wake_then_forward(
|
||||
f"http://{JEAN_PROXY_HOST}:{JEAN_PROXY_PORT}/v1/chat/completions",
|
||||
data, fwd_headers
|
||||
)
|
||||
|
||||
if not ensure_vram_for_backend("jean"):
|
||||
return jsonify({"error": "Failed to prepare VRAM for Jean"}), 503
|
||||
|
||||
Reference in New Issue
Block a user