refacto: dashboard Flask Blueprints + templates Jinja2, engine run/lore_enricher mis a jour

This commit is contained in:
2026-06-17 14:10:01 +00:00
parent ae6356ded4
commit ea8e24e238
24 changed files with 2457 additions and 1850 deletions
+150
View File
@@ -0,0 +1,150 @@
import os
import json
import time
import threading
import subprocess
from flask import Blueprint, render_template, jsonify, request
from ..db import query_one, get_all_sessions
from ..config import SIM_CONFIGS_DIR
from .. import process_mgr as pm
bp = Blueprint("tools", __name__)
# ── Stress test state ─────────────────────────────────────────────────────────
_stress_state = {
"running": False, "started_at": None, "mode": None,
"rounds": None, "pnj_count": None,
"output": [], "returncode": None, "error": None,
}
_stress_lock = threading.Lock()
def _run_stress_bg(mode: str, rounds: int, pnj_count: int):
global _stress_state
ollama_url = os.getenv("OLLAMA_URL", "http://host.docker.internal:11434")
model_mj = os.getenv("MODEL_MJ", "qwen2.5:14b")
model_pnj = os.getenv("MODEL_PNJ", "qwen2.5:7b")
results_path = os.path.join(SIM_CONFIGS_DIR, "crash_results.json")
cmd = [
"python3", "/app/tools/llm_crash_test.py",
"--mode", mode, "--rounds", str(rounds), "--pnj-count", str(pnj_count),
]
env = {**os.environ,
"OLLAMA_URL": ollama_url, "MODEL_MJ": model_mj, "MODEL_PNJ": model_pnj,
"CRASH_RESULTS_PATH": results_path, "PYTHONUNBUFFERED": "1"}
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
text=True, env=env)
for line in proc.stdout:
with _stress_lock:
_stress_state["output"].append(line.rstrip())
proc.wait()
with _stress_lock:
_stress_state["returncode"] = proc.returncode
_stress_state["running"] = False
except Exception as e:
with _stress_lock:
_stress_state["error"] = str(e)
_stress_state["running"] = False
# ── Routes ────────────────────────────────────────────────────────────────────
@bp.route("/tools")
def tools():
all_sessions = get_all_sessions()
sid = int(request.args.get("sid", all_sessions[-1]["id"] if all_sessions else 1))
session = query_one("SELECT * FROM sessions WHERE id = %s", (sid,))
return render_template("tools.html", tab="tools", current_sid=sid,
all_sessions=all_sessions, session=session,
flash_msg=None, flash_ok=True)
@bp.route("/api/stress-test/run", methods=["POST"])
def api_stress_run():
global _stress_state
with _stress_lock:
if _stress_state["running"]:
return jsonify({"error": "Un test est déjà en cours.", "running": True})
mode = request.json.get("mode", "simultane")
rounds = int(request.json.get("rounds", 5))
pnj_count = int(request.json.get("pnj_count", 2))
_stress_state = {
"running": True, "started_at": time.strftime("%H:%M:%S"),
"mode": mode, "rounds": rounds, "pnj_count": pnj_count,
"output": [], "returncode": None, "error": None,
}
t = threading.Thread(target=_run_stress_bg, args=(mode, rounds, pnj_count), daemon=True)
t.start()
return jsonify({"ok": True, "message": f"Test '{mode}' {rounds} rounds lancé."})
@bp.route("/api/stress-test/status")
def api_stress_status():
with _stress_lock:
return jsonify({
"running": _stress_state["running"],
"started_at": _stress_state["started_at"],
"mode": _stress_state["mode"],
"rounds": _stress_state["rounds"],
"output": _stress_state["output"][-60:],
"returncode": _stress_state["returncode"],
"error": _stress_state["error"],
})
@bp.route("/api/stress-test/results")
def api_stress_results():
import datetime
path = os.path.join(SIM_CONFIGS_DIR, "crash_results.json")
if not os.path.exists(path):
return jsonify({"error": "Aucun résultat. Lancez d'abord le test."})
try:
mtime = os.path.getmtime(path)
file_date = datetime.datetime.fromtimestamp(mtime).strftime("%d/%m/%Y %H:%M:%S")
with open(path) as f:
data = json.load(f)
data["file_date"] = file_date
return jsonify(data)
except Exception as e:
return jsonify({"error": f"Erreur lecture : {e}"})
@bp.route("/api/proc/<name>/run", methods=["POST"])
def proc_run(name):
if name == "mix":
if pm.proc_is_running("mix"):
return jsonify({"error": "Mix déjà en cours"}), 400
data = request.get_json(silent=True) or {}
count = int(data.get("count", 10))
pm.proc_mix_start(count)
return jsonify({"ok": True, "label": f"Mode MIX — {count if count else ''}× chaque"})
if name not in pm._PROC_DEFS:
return jsonify({"error": f"Processus inconnu: {name}"}), 400
if pm.proc_is_running(name):
return jsonify({"error": "Déjà en cours"}), 400
defn = pm._PROC_DEFS[name]
data = request.get_json(silent=True) or {}
count = int(data.get("count", 1))
cmd = list(defn["cmd"])
if name == "sim_enricher" and data.get("type"):
try: cmd[cmd.index("all")] = data["type"]
except ValueError: pass
if name == "lore_enricher" and data.get("faction"):
try: cmd[cmd.index("all")] = data["faction"]
except ValueError: pass
pm.proc_start(name, cmd, defn.get("env_extra", {}), count)
return jsonify({"ok": True, "label": defn["label"]})
@bp.route("/api/proc/<name>/status")
def proc_status(name):
return jsonify(pm.proc_get_state(name))
@bp.route("/api/proc/<name>/stop", methods=["POST"])
def proc_stop_route(name):
pm.proc_stop(name)
return jsonify({"ok": True})