fix(health): drop live LLM ping — scan agent.log instead (38s -> 0.1s)

Every /api/xmpp/health fetch ran a real LLM call (22k token system prompt
each). Dashboard refreshes every 10s -> thousands of paid LLM calls/day,
plus 38s latency hanging the health tab on '加载中...'.

LLM health is now derived from the gateway's own agent.log (zero cost,
more accurate than synthetic ping — real traffic results):
- last 'API call #N latency=Xs' -> ok
- last 'API call failed ... HTTP 429...' -> error with summary
- health() runtime 38s -> 0.1s; endpoint 38s -> 0.097s
This commit is contained in:
hmo
2026-07-20 00:21:49 +08:00
parent 058c42ce27
commit 2de527b883
2 changed files with 78 additions and 14 deletions
+20
View File
@@ -0,0 +1,20 @@
import ast, sys
for p in ['/home/hmo/MoFin/xmpp_logger.py']:
try:
ast.parse(open(p).read())
print('SYNTAX OK:', p)
except SyntaxError as e:
print('SYNTAX ERROR:', p, e)
sys.exit(1)
sys.path.insert(0, '/home/hmo/MoFin')
import xmpp_logger as x
import time, json
print('== _scan_agent_log ==')
print(json.dumps(x._scan_agent_log(time.time()), ensure_ascii=False))
print('== health (no LLM ping) ==')
import time as t
t0 = t.time()
h = x.health()
print(f'took {t.time()-t0:.1f}s')
print('llm_provider:', h.get('llm_provider'))
print('status:', h.get('status'))
+58 -14
View File
@@ -338,20 +338,9 @@ def health():
except Exception: pass
result["gateways"][name] = {"port": port, "alive": ok}
# 测试知微的 LLM 调用是否可达
if result["gateways"].get("zhiwei", {}).get("alive"):
try:
import urllib.request
payload = json.dumps({"model": "deepseek-v4-flash", "messages": [{"role": "user", "content": "ping"}],
"max_tokens": 5, "stream": False}).encode()
req = urllib.request.Request("http://127.0.0.1:8643/v1/chat/completions",
data=payload, headers={"Content-Type": "application/json",
"Authorization": "Bearer hermes123"})
urllib.request.urlopen(req, timeout=90)
result["llm_provider"] = {"status": "ok", "latency": "fast"}
except Exception as e:
result["llm_provider"] = {"status": "timeout" if "timeout" in str(e).lower() else "error",
"error": str(e)[:150]}
# LLM 健康:扫 gateway agent.log 最近一次真实调用结果(零成本,不发 LLM 请求)
# agent.log 里每次真实调用都有记录:成功 "API call #N: ... latency=Xs" / 失败 "HTTP 429..."
result["llm_provider"] = _scan_agent_log(now_epoch)
except Exception as e:
result["gateways"] = {"error": str(e)[:100]}
@@ -376,6 +365,61 @@ def health():
return result
AGENT_LOG = Path("/home/hmo/.hermes/profiles/position-analyst/logs/agent.log")
def _scan_agent_log(now_epoch, tail_lines=300):
"""扫 gateway agent.log 尾部,取最近一次真实 LLM 调用的结果。
成功行: "2026-07-19 20:04:42,880 INFO ... API call #4: ... latency=16.0s ..."
失败行: "2026-07-19 19:35:59 WARNING ... API call failed ... HTTP 429: Weekly usage limit reached ..."
返回 {"status": "ok"|"error"|"unknown", "latency"/"error", "age_sec"}
"""
if not AGENT_LOG.exists():
return {"status": "unknown", "error": "agent.log not found"}
try:
# 只读尾部(大文件不全读)
size = AGENT_LOG.stat().st_size
with open(AGENT_LOG, "rb") as f:
f.seek(max(0, size - 64 * 1024))
chunk = f.read().decode("utf-8", errors="replace")
lines = [l for l in chunk.splitlines() if l.strip()][-tail_lines:]
except Exception as e:
return {"status": "unknown", "error": str(e)[:80]}
last_ok = None # (epoch, latency_str)
last_fail = None # (epoch, summary)
for line in lines:
m_ts = _re.search(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line)
if not m_ts:
continue
try:
ts = datetime.strptime(m_ts.group(1), "%Y-%m-%d %H:%M:%S").timestamp()
except Exception:
continue
m_ok = _re.search(r"API call #\d+.*latency=([\d.]+)s", line)
if m_ok:
last_ok = (ts, m_ok.group(1))
continue
if "API call failed" in line and ("summary=" in line or "after" in line):
m_err = _re.search(r"summary=(.+?)$", line)
summary = (m_err.group(1) if m_err else line)[:150]
last_fail = (ts, summary)
if not last_ok and not last_fail:
return {"status": "unknown", "error": "no LLM calls in log"}
if last_fail and (not last_ok or last_fail[0] > last_ok[0]):
return {"status": "error",
"error": last_fail[1],
"age_sec": int(now_epoch - last_fail[0]),
"source": "agent.log"}
return {"status": "ok",
"latency": f"{last_ok[1]}s",
"age_sec": int(now_epoch - last_ok[0]),
"source": "agent.log"}
def _fetch_keys():
"""从 AgentsMeeting Dashboard 获取可用 API Key 列表"""
try: