refacto: dashboard Flask Blueprints + templates Jinja2, engine run/lore_enricher mis a jour
This commit is contained in:
@@ -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})
|
||||
Reference in New Issue
Block a user