feat: API key availability monitoring — auto-select best key from AgentsMeeting

This commit is contained in:
hmo
2026-07-19 14:41:18 +08:00
parent 6398f595b2
commit f791d8dee7
2 changed files with 80 additions and 2 deletions
+11
View File
@@ -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
View File
@@ -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: