From 6398f595b2110adf8d9d6e8427e88c774ac1b078 Mon Sep 17 00:00:00 2001 From: hmo Date: Sun, 19 Jul 2026 14:18:45 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20auto-heal=20pipeline=20=E2=80=94=20dete?= =?UTF-8?q?ct+restart=20gateway/bot/ejabberd=20on=20failure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.py | 14 ++++++++++++++ xmpp_logger.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/server.py b/server.py index e94a4f63..1b4c86fb 100644 --- a/server.py +++ b/server.py @@ -1315,6 +1315,20 @@ def api_xmpp_stats(): return jsonify({"today": {"sent": 0, "failed": 0, "latency_avg": 0}, "week": {"sent": 0, "failed": 0}}) +@app.route("/api/xmpp/autoheal", methods=["GET", "POST"]) +def api_xmpp_autoheal(): + """自愈:检测异常并自动修复。GET=查看状态, POST=执行修复""" + try: + from xmpp_logger import auto_heal + if request.method == "POST": + result = auto_heal() + return jsonify(result) + else: + return jsonify({"usage": "POST to trigger auto-heal"}) + except ImportError: + return jsonify({"error": "xmpp_logger not available"}), 500 + + # 注册提示词管理路由 register_routes(app) diff --git a/xmpp_logger.py b/xmpp_logger.py index aadc1bb8..977d2683 100644 --- a/xmpp_logger.py +++ b/xmpp_logger.py @@ -9,6 +9,7 @@ """ import json import time as _time +import subprocess as _sp from pathlib import Path from datetime import datetime, timedelta @@ -230,3 +231,46 @@ def health(): elif la > 0: result["status"] = "ok" return result + + +def auto_heal(): + """自愈:检测并尝试修复 XMPP 通信问题。""" + h = health() + actions = [] + + # 1. LLM Provider 超时 → 重启 Hermes Gateway + if h.get("llm_provider", {}).get("status") in ("timeout", "error"): + gw = h.get("gateways", {}).get("zhiwei", {}) + if gw.get("alive"): + try: + r = _sp.run(["sudo", "-n", "systemctl", "restart", "hermes-gw-pa"], + capture_output=True, timeout=30, text=True) + ok = r.returncode == 0 + actions.append({"action": "restart_hermes_gateway", "target": "position-analyst", + "success": ok, "detail": "restarted" if ok else r.stderr[:100]}) + except Exception as e: + actions.append({"action": "restart_hermes_gateway", "target": "position-analyst", + "success": False, "detail": str(e)[:100]}) + + # 2. Bot 无出站 → 重启知微 Bot + ba = h.get("bot_activity", {}) + if ba.get("inbound", 0) > 0 and ba.get("outbound", 0) == 0 and ba.get("errors", 0) > 0: + try: + r = _sp.run(["sudo", "-n", "systemctl", "restart", "xmpp-zhiwei"], capture_output=True, timeout=30, text=True) + actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", + "success": r.returncode == 0, "detail": "restarted" if r.returncode == 0 else r.stderr[:100]}) + except Exception as e: + actions.append({"action": "restart_zhiwei_bot", "target": "xmpp-zhiwei", + "success": False, "detail": str(e)[:100]}) + + # 3. ejabberd → 重启容器 + if h.get("ejabberd", "") and "Up" not in str(h["ejabberd"]): + try: + r = _sp.run(["sudo", "-n", "docker", "restart", "ejabberd"], capture_output=True, timeout=30, text=True) + actions.append({"action": "restart_ejabberd", "target": "ejabberd", + "success": r.returncode == 0, "detail": "restarted" if r.returncode == 0 else r.stderr[:100]}) + except Exception as e: + actions.append({"action": "restart_ejabberd", "target": "ejabberd", + "success": False, "detail": str(e)[:100]}) + + return {"actions": actions, "status": h.get("status", "unknown")}