fix(tui): setup.runtime_check resolves like session creation and reports the model

Without an explicit `provider`, `setup.runtime_check` called the strict
`resolve_runtime_provider(requested=None)` while `_make_agent` goes through
`_resolve_agent_model_runtime` (startup model + provider pin, then the
configured fallback chain). With the primary blocked and a working fallback
entry the probe answered ok:False (the primary's auth error) and the Desktop
showed onboarding for a backend whose sessions built fine. The probe also
reported `model: null` because the resolver never populates that key.

The default case now runs the session builder's resolver; an explicit
`provider` stays a strict single-provider check (onboarding verifies the
provider just connected — another provider's fallback must not mask a failed
connection) and resolves against the startup model like the session would.
Both branches report the selected model.

Fixes #111775
This commit is contained in:
KoNit-K
2026-09-15 11:54:23 -07:00
committed by Teknium
parent 05e82b741d
commit 6f975b768e
2 changed files with 38 additions and 11 deletions
+22 -4
View File
@@ -9158,9 +9158,10 @@ def test_probe_credentials_allows_keyless_custom_runtime():
def test_setup_runtime_check_rejects_empty_runtime_key(monkeypatch):
monkeypatch.setattr("hermes_cli.main._has_any_provider_configured", lambda **_kw: True)
monkeypatch.setattr(server, "_resolve_startup_runtime", lambda: ("openrouter/test-model", None))
monkeypatch.setattr(
"hermes_cli.runtime_provider.resolve_runtime_provider",
lambda requested=None: {
lambda requested=None, **_kw: {
"provider": "openrouter",
"api_key": "",
"source": "env/config",
@@ -9172,7 +9173,7 @@ def test_setup_runtime_check_rejects_empty_runtime_key(monkeypatch):
assert resp["result"] == {
"ok": False,
"provider": "openrouter",
"model": None,
"model": "openrouter/test-model",
"source": "env/config",
"error": "No usable credentials found for openrouter.",
}
@@ -9182,7 +9183,7 @@ def test_setup_runtime_check_allows_no_key_custom_runtime(monkeypatch):
monkeypatch.setattr("hermes_cli.main._has_any_provider_configured", lambda **_kw: True)
monkeypatch.setattr(
"hermes_cli.runtime_provider.resolve_runtime_provider",
lambda requested=None: {
lambda requested=None, **_kw: {
"provider": "custom",
"api_key": "no-key-required",
"source": "env/config",
@@ -9199,7 +9200,7 @@ def test_setup_runtime_check_rejects_implicit_bedrock_when_unconfigured(monkeypa
monkeypatch.setattr("hermes_cli.main._has_any_provider_configured", lambda **_kw: False)
monkeypatch.setattr(
"hermes_cli.runtime_provider.resolve_runtime_provider",
lambda requested=None: {
lambda requested=None, **_kw: {
"provider": "bedrock",
"api_key": "aws-sdk",
"source": "iam-role",
@@ -9245,6 +9246,23 @@ def test_setup_runtime_check_honors_requested_provider(monkeypatch):
assert default["result"]["provider"] == "anthropic"
def test_setup_runtime_check_reports_target_model_on_credential_failure(monkeypatch):
"""#111775: the probe names the model session creation would use, never ``model: null``."""
monkeypatch.setattr("hermes_cli.main._has_any_provider_configured", lambda **_kw: True)
monkeypatch.setattr(server, "_resolve_startup_runtime", lambda: ("z-ai/glm-5.2", None))
monkeypatch.setattr(
"hermes_cli.runtime_provider.resolve_runtime_provider",
lambda *, requested=None, target_model=None: {
"provider": "zai", "api_key": "", "source": "env/config"
},
)
resp = server.handle_request({"id": "1", "method": "setup.runtime_check", "params": {}})
assert resp["result"]["ok"] is False
assert resp["result"]["model"] == "z-ai/glm-5.2"
def test_setup_readiness_scopes_to_requested_profile(monkeypatch, tmp_path):
"""#94071: the Desktop preflights a freshly created bot on its target
backend. ``profile`` binds THAT profile's home + .env — launch-process
+16 -7
View File
@@ -294,10 +294,15 @@ def _(rid, params: dict) -> dict:
@method("setup.runtime_check")
def _(rid, params: dict) -> dict:
"""Strict provider check via the same resolve_runtime_provider() the agent uses on session
creation (setup.status is True if ANY provider auth state is discoverable): ok=False + the auth
error when the model can't be served, so UIs surface onboarding before a doomed prompt.
``profile`` answers for THAT profile's pin and ``.env``; unknown -> ``ok=False``."""
"""Readiness probe for the session a client is about to open (setup.status is True if ANY
provider auth state is discoverable): ok=False + the auth error when the model can't be served,
so UIs surface onboarding before a doomed prompt. Without ``provider`` it runs the SAME
resolver as session creation (``_resolve_agent_model_runtime``: startup model + provider pin,
then the configured fallback chain) — a probe that ignores the chain shows onboarding for a
backend whose sessions build fine. An explicit ``provider`` stays a strict single-provider
check so onboarding can verify the provider just connected without another provider's
fallback masking a failed connection. ``profile`` answers for THAT profile's pin and ``.env``;
unknown -> ``ok=False``."""
try:
from hermes_cli.runtime_provider import resolve_runtime_provider
from hermes_cli.auth import has_usable_secret
@@ -305,13 +310,17 @@ def _(rid, params: dict) -> dict:
requested = str(params.get("provider") or "").strip() or None
def probe(profile, scoped):
runtime = resolve_runtime_provider(requested=requested)
if requested:
model, _startup_provider = _resolve_startup_runtime()
runtime = resolve_runtime_provider(requested=requested, target_model=model or None)
else:
model, runtime = _resolve_agent_model_runtime(None, None)
provider_configured = bool(_has_any_provider_configured(strict_profile_scope=bool(profile)))
provider = runtime.get("provider") or "provider"
source = str(runtime.get("source") or "")
def fail(error, src):
return {"ok": False, "provider": provider, "model": runtime.get("model"),
return {"ok": False, "provider": provider, "model": model,
"source": src, "error": error, **scoped}
if (not provider_configured and provider == "bedrock"
and source in {"iam-role", "aws-sdk-default-chain"}):
@@ -324,7 +333,7 @@ def _(rid, params: dict) -> dict:
from hermes_cli.anon_auth import route_is_welcome_host
# free_tier is keyed on the SELECTED route (the welcome host serves only nous/welcome), not
# on profile state: a paid Nous key beside a free-tier identity must not read as free.
return {"ok": True, "provider": runtime.get("provider"), "model": runtime.get("model"),
return {"ok": True, "provider": runtime.get("provider"), "model": model,
"source": runtime.get("source"),
"free_tier": provider == "nous" and route_is_welcome_host(runtime.get("base_url")),
**scoped}