test_hermes_api.py 5.25 KB
#!/usr/bin/env python3
"""
Hermes API 调用测试 — 验证 daemon 到 hermes OpenAI-compatible API 的 HTTP 链路。
在 Ubuntu proot 或 Termux 环境内执行。
"""
import subprocess, json, sys, os, time, urllib.request, urllib.error

CONFIG_PATH = "config.json"
REPORT = {"test": "hermes_api", "steps": {}, "overall": None}


def log_step(name, ok, detail=""):
    status = "[OK]" if ok else "[FAIL]"
    print(f"  {status} {name}")
    if detail:
        print(f"       {detail}")
    REPORT["steps"][name] = {"ok": ok, "detail": detail}
    return ok


def call_hermes_api(api_url, message, session_id, timeout=60):
    """Call hermes OpenAI-compatible API and parse SSE response."""
    payload = json.dumps({
        "model": "hermes-agent",
        "messages": [
            {"role": "user", "content": message}
        ],
        "stream": True,
        "session_id": session_id
    }).encode("utf-8")

    req = urllib.request.Request(
        api_url,
        data=payload,
        headers={"Content-Type": "application/json", "Accept": "text/event-stream"},
        method="POST"
    )

    try:
        full_reply = []
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            buffer = ""
            for chunk in resp:
                chunk_str = chunk.decode("utf-8", errors="replace")
                buffer += chunk_str
                while "\n\n" in buffer:
                    block, buffer = buffer.split("\n\n", 1)
                    data_str = None
                    for line in block.strip().split("\n"):
                        if line.startswith("data:"):
                            data_str = line[5:].strip()
                    if data_str:
                        if data_str == "[DONE]":
                            break
                        try:
                            obj = json.loads(data_str)
                            delta = obj.get("choices", [{}])[0].get("delta", {})
                            content = delta.get("content", "")
                            if content:
                                full_reply.append(content)
                        except json.JSONDecodeError:
                            pass
        return True, "".join(full_reply)
    except urllib.error.URLError as e:
        return False, f"URLError: {e}"
    except Exception as e:
        return False, str(e)


def main():
    print("=" * 60)
    print("Hermes API 调用测试 (OpenAI-compatible)")
    print("=" * 60)

    config = {}
    if os.path.exists(CONFIG_PATH):
        with open(CONFIG_PATH) as f:
            config = json.load(f)

    all_ok = True
    hermes_cfg = config.get("hermes", {})
    api_url = hermes_cfg.get("api_url", "http://localhost:8642/v1/chat/completions")
    timeout = hermes_cfg.get("timeout_sec", 60)

    # Step 1: Check hermes server is reachable
    print("\n[Step 1] 检查 hermes 服务可达性")
    try:
        health_url = api_url.replace("/v1/chat/completions", "/health")
        req = urllib.request.Request(health_url, method="GET")
        with urllib.request.urlopen(req, timeout=5) as resp:
            health_ok = resp.status == 200
            body = resp.read().decode("utf-8", errors="replace")[:100]
    except Exception as e:
        health_ok = False
        body = str(e)
    all_ok &= log_step("hermes_reachable", health_ok, body)

    if not health_ok:
        print("\n  [提示] hermes 服务未启动或端口不对。请确认:")
        print(f"       1. hermes gateway 已启动 (python3 -m gateway.run)")
        print(f"       2. api_server 已启用 (config.yaml platforms.api_server.enabled=true)")
        print(f"       3. 监听端口与 {api_url} 一致")
        save_report()
        return 1

    # Step 2: Send a test message
    print("\n[Step 2] 发送测试消息到 hermes")
    test_msg = "你好,这是一个来自 daemon 的测试消息。请回复一句简短的话。"
    session_id = f"test-{int(time.time())}"
    print(f"       sessionId: {session_id}")
    print(f"       message: {test_msg[:60]}")

    ok, result = call_hermes_api(api_url, test_msg, session_id, timeout)
    detail = f"回复: {result[:80]}" if result else f"错误: {result}"
    all_ok &= log_step("hermes_chat", ok, detail)

    # Step 3: Context persistence test (same session)
    if ok and result:
        print("\n[Step 3] 上下文持久化测试(同一 session)")
        followup = "刚才你说了什么?请重复一遍。"
        ok2, result2 = call_hermes_api(api_url, followup, session_id, timeout)
        has_context = ok2 and result2 and (result[:30] in result2 or "刚才" in result2 or "说了" in result2)
        all_ok &= log_step("context_persist", has_context,
                           f"回复: {result2[:80] if result2 else 'None'}" if ok2 else f"失败: {result2}")

    # Summary
    REPORT["overall"] = "PASS" if all_ok else "FAIL"
    REPORT["timestamp"] = subprocess.check_output(["date", "-Iseconds"]).decode().strip()

    save_report()

    print("\n" + "=" * 60)
    print(f"结果: {REPORT['overall']}")
    print("=" * 60)
    print("\n详细报告: test_report_hermes_api.json")
    return 0 if all_ok else 1


def save_report():
    with open("test_report_hermes_api.json", "w") as f:
        json.dump(REPORT, f, indent=2, ensure_ascii=False)


if __name__ == "__main__":
    sys.exit(main())