Files

151 lines
5.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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})