feat: API key availability monitoring — auto-select best key from AgentsMeeting
This commit is contained in:
@@ -1329,6 +1329,17 @@ def api_xmpp_autoheal():
|
||||
return jsonify({"error": "xmpp_logger not available"}), 500
|
||||
|
||||
|
||||
@app.route("/api/xmpp/keys")
|
||||
def api_xmpp_keys():
|
||||
"""API Key 可用性:从 AgentsMeeting 获取并选择最佳 key"""
|
||||
try:
|
||||
from xmpp_logger import best_key
|
||||
bk = best_key()
|
||||
return jsonify({"best_key": bk} if bk else {"error": "no keys available"})
|
||||
except ImportError:
|
||||
return jsonify({"error": "xmpp_logger not available"}), 500
|
||||
|
||||
|
||||
# 注册提示词管理路由
|
||||
register_routes(app)
|
||||
|
||||
|
||||
+69
-2
@@ -10,6 +10,7 @@
|
||||
import json
|
||||
import time as _time
|
||||
import subprocess as _sp
|
||||
import urllib.request as _ur
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
@@ -230,9 +231,75 @@ def health():
|
||||
elif er > 50: result["status"] = "degraded"
|
||||
elif la > 0: result["status"] = "ok"
|
||||
|
||||
# 5. API Key 可用性
|
||||
bk = best_key()
|
||||
if bk:
|
||||
result["best_key"] = bk
|
||||
if bk["issues"]:
|
||||
result["status"] = "degraded"
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _fetch_keys():
|
||||
"""从 AgentsMeeting Dashboard 获取可用 API Key 列表"""
|
||||
try:
|
||||
req = _ur.Request("http://127.0.0.1:5803/api/keys")
|
||||
resp = _ur.urlopen(req, timeout=5)
|
||||
data = json.loads(resp.read())
|
||||
return data.get("keys", []) if data.get("ok") else []
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def best_key():
|
||||
"""选择最佳可用 API Key。
|
||||
|
||||
优先级: rolling ok > weekly ok > monthly ok > 最低 usage
|
||||
返回: {"key_id": "key6", "label": "...", "usage": {...}, "reason": "..."}
|
||||
"""
|
||||
keys = _fetch_keys()
|
||||
if not keys:
|
||||
return None
|
||||
|
||||
def score(k):
|
||||
"""分数越低越好"""
|
||||
s = 0
|
||||
r = k.get("rolling", {})
|
||||
w = k.get("weekly", {})
|
||||
m = k.get("monthly", {})
|
||||
# rate-limited 惩罚
|
||||
if r.get("status") != "ok": s += 1000
|
||||
if w.get("status") != "ok": s += 100
|
||||
if m.get("status") != "ok": s += 10
|
||||
# usage 越高越差
|
||||
s += r.get("usage_percent", 0) * 0.01
|
||||
s += w.get("usage_percent", 0) * 0.001
|
||||
s += m.get("usage_percent", 0) * 0.0001
|
||||
# session_expired 惩罚
|
||||
if k.get("session_expired"): s += 500
|
||||
return s
|
||||
|
||||
best = min(keys, key=score)
|
||||
reasons = []
|
||||
if best["rolling"]["status"] != "ok": reasons.append(f"rolling {best['rolling']['usage_percent']}%")
|
||||
if best["weekly"]["status"] != "ok": reasons.append(f"weekly {best['weekly']['usage_percent']}%")
|
||||
if best["monthly"]["status"] != "ok": reasons.append(f"monthly {best['monthly']['usage_percent']}%")
|
||||
if best.get("session_expired"): reasons.append("session_expired")
|
||||
|
||||
return {
|
||||
"key_id": best["key_id"],
|
||||
"label": best["label"],
|
||||
"masked": best.get("key_masked", ""),
|
||||
"rolling": best["rolling"],
|
||||
"weekly": best["weekly"],
|
||||
"monthly": best["monthly"],
|
||||
"session_expired": best.get("session_expired", False),
|
||||
"issues": reasons,
|
||||
"total_keys": len(keys),
|
||||
}
|
||||
|
||||
|
||||
def auto_heal():
|
||||
"""自愈:检测并尝试修复 XMPP 通信问题。"""
|
||||
h = health()
|
||||
@@ -256,7 +323,7 @@ def auto_heal():
|
||||
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)
|
||||
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:
|
||||
@@ -266,7 +333,7 @@ def auto_heal():
|
||||
# 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)
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user