fix: tighten hermes doctor issue summary

This commit is contained in:
Keane Yan 2026-04-17 10:47:16 +08:00
parent 5dda4cab41
commit 20853c23e2
2 changed files with 104 additions and 23 deletions

View file

@ -454,11 +454,7 @@ def run_doctor(args):
print(color("◆ Auth Providers", Colors.CYAN, Colors.BOLD))
try:
from hermes_cli.auth import (
get_nous_auth_status,
get_codex_auth_status,
get_gemini_oauth_auth_status,
)
from hermes_cli.auth import get_nous_auth_status, get_codex_auth_status
nous_status = get_nous_auth_status()
if nous_status.get("logged_in"):
@ -473,20 +469,6 @@ def run_doctor(args):
check_warn("OpenAI Codex auth", "(not logged in)")
if codex_status.get("error"):
check_info(codex_status["error"])
gemini_status = get_gemini_oauth_auth_status()
if gemini_status.get("logged_in"):
email = gemini_status.get("email") or ""
project = gemini_status.get("project_id") or ""
pieces = []
if email:
pieces.append(email)
if project:
pieces.append(f"project={project}")
suffix = f" ({', '.join(pieces)})" if pieces else ""
check_ok("Google Gemini OAuth", f"(logged in{suffix})")
else:
check_warn("Google Gemini OAuth", "(not logged in)")
except Exception as e:
check_warn("Auth provider status", f"(could not check: {e})")
@ -1048,10 +1030,24 @@ def run_doctor(args):
else:
check_warn(item["name"], "(system dependency not met)")
# Count disabled tools with API key requirements
api_disabled = [u for u in unavailable if (u.get("missing_vars") or u.get("env_vars"))]
# Only summarize missing API keys for toolsets actually enabled in the CLI.
# Otherwise default-off / disabled toolsets (for example rl) create noisy
# false positives in the final "Found N issue(s)" summary.
try:
from hermes_cli.config import load_config
from hermes_cli.tools_config import _get_platform_tools
enabled_cli_toolsets = set(_get_platform_tools(load_config(), "cli", include_default_mcp_servers=False))
except Exception:
enabled_cli_toolsets = set()
api_disabled = [
u for u in unavailable
if (u.get("missing_vars") or u.get("env_vars"))
and (not enabled_cli_toolsets or u.get("name") in enabled_cli_toolsets)
]
if api_disabled:
issues.append("Run 'hermes setup' to configure missing API keys for full tool access")
issues.append("Run 'hermes setup' to configure missing API keys for enabled toolsets")
except Exception as e:
check_warn("Could not check tool availability", f"({e})")

View file

@ -302,7 +302,7 @@ def test_run_doctor_kimi_cn_env_is_detected_and_probe_is_null_safe(monkeypatch,
home = tmp_path / ".hermes"
home.mkdir(parents=True, exist_ok=True)
(home / "config.yaml").write_text("memory: {}\n", encoding="utf-8")
(home / ".env").write_text("KIMI_CN_API_KEY=sk-test\n", encoding="utf-8")
(home / ".env").write_text("KIMI_CN_API_KEY=***", encoding="utf-8")
project = tmp_path / "project"
project.mkdir(exist_ok=True)
@ -345,6 +345,91 @@ def test_run_doctor_kimi_cn_env_is_detected_and_probe_is_null_safe(monkeypatch,
assert any(url == "https://api.moonshot.cn/v1/models" for url, _, _ in calls)
def test_run_doctor_ignores_missing_api_keys_for_disabled_toolsets(monkeypatch, tmp_path):
home = tmp_path / ".hermes"
home.mkdir(parents=True, exist_ok=True)
(home / "config.yaml").write_text("memory: {}\n", encoding="utf-8")
project = tmp_path / "project"
project.mkdir(exist_ok=True)
monkeypatch.setattr(doctor_mod, "HERMES_HOME", home)
monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", project)
monkeypatch.setattr(doctor_mod, "_DHH", str(home))
fake_model_tools = types.SimpleNamespace(
check_tool_availability=lambda *a, **kw: (
["web"],
[{"name": "rl", "missing_vars": ["TINKER_API_KEY", "WANDB_API_KEY"]}],
),
TOOLSET_REQUIREMENTS={"web": {"name": "web"}},
)
monkeypatch.setitem(sys.modules, "model_tools", fake_model_tools)
try:
from hermes_cli import auth as _auth_mod
monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {})
monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {})
import httpx
monkeypatch.setattr(httpx, "get", lambda *a, **kw: types.SimpleNamespace(status_code=200))
from hermes_cli import config as _config_mod
from hermes_cli import tools_config as _tools_mod
monkeypatch.setattr(_config_mod, "load_config", lambda: {"platform_toolsets": {"cli": ["web"]}})
monkeypatch.setattr(_tools_mod, "_get_platform_tools", lambda *a, **kw: {"web"})
except Exception:
pass
import io, contextlib
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
doctor_mod.run_doctor(Namespace(fix=False))
out = buf.getvalue()
assert "⚠ rl" in out or "rl" in out
assert "Run 'hermes setup' to configure missing API keys for enabled toolsets" not in out
def test_run_doctor_reports_missing_api_keys_for_enabled_toolsets(monkeypatch, tmp_path):
home = tmp_path / ".hermes"
home.mkdir(parents=True, exist_ok=True)
(home / "config.yaml").write_text("memory: {}\n", encoding="utf-8")
project = tmp_path / "project"
project.mkdir(exist_ok=True)
monkeypatch.setattr(doctor_mod, "HERMES_HOME", home)
monkeypatch.setattr(doctor_mod, "PROJECT_ROOT", project)
monkeypatch.setattr(doctor_mod, "_DHH", str(home))
fake_model_tools = types.SimpleNamespace(
check_tool_availability=lambda *a, **kw: (
[],
[{"name": "web", "missing_vars": ["OPENROUTER_API_KEY"]}],
),
TOOLSET_REQUIREMENTS={},
)
monkeypatch.setitem(sys.modules, "model_tools", fake_model_tools)
try:
from hermes_cli import auth as _auth_mod
monkeypatch.setattr(_auth_mod, "get_nous_auth_status", lambda: {})
monkeypatch.setattr(_auth_mod, "get_codex_auth_status", lambda: {})
import httpx
monkeypatch.setattr(httpx, "get", lambda *a, **kw: types.SimpleNamespace(status_code=200))
from hermes_cli import config as _config_mod
from hermes_cli import tools_config as _tools_mod
monkeypatch.setattr(_config_mod, "load_config", lambda: {"platform_toolsets": {"cli": ["web"]}})
monkeypatch.setattr(_tools_mod, "_get_platform_tools", lambda *a, **kw: {"web"})
except Exception:
pass
import io, contextlib
buf = io.StringIO()
with contextlib.redirect_stdout(buf):
doctor_mod.run_doctor(Namespace(fix=False))
out = buf.getvalue()
assert "Run 'hermes setup' to configure missing API keys for enabled toolsets" in out
@pytest.mark.parametrize("base_url", [None, "https://opencode.ai/zen/go/v1"])
def test_run_doctor_opencode_go_skips_invalid_models_probe(monkeypatch, tmp_path, base_url):
home = tmp_path / ".hermes"