From 1f702c7569d00aa90fccd8bffee1568a5bea2440 Mon Sep 17 00:00:00 2001 From: hermes-agent Date: Thu, 7 May 2026 17:07:48 +0000 Subject: [PATCH] stage-313 absorb: gate _resolve_configured_provider_id alias resolution + harden bootstrap test isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two in-stage fixes for v0.51.19 batch: 1) api/config.py — add resolve_alias=False param to _resolve_configured_provider_id() and pass it from resolve_model_provider(). The PR #1818 swap from _resolve_provider_alias() to _resolve_configured_provider_id() was correct for active-provider/badge surfaces but broke #1625's local-server-provider literal-preservation contract: 'ollama' → 'custom' and 'lm-studio' → 'lmstudio' alias-collapse caused _LOCAL_SERVER_PROVIDERS membership check to miss, breaking the model-id full-path preservation for LM Studio/Ollama. The new flag preserves the raw provider value when called from resolve_model_provider, and named-custom-slug + base-url fallback both still run unchanged. 2) tests/test_bootstrap_discover_agent.py — pin Path.home() in _isolate_discover_agent_dir so the hard-coded 'Path.home() / .hermes / hermes-agent' / 'Path.home() / hermes-agent' candidates in discover_agent_dir() can't pick up the dev machine's real install. The original PR #1817 isolation helper covered HERMES_HOME, HERMES_WEBUI_AGENT_DIR, and REPO_ROOT but missed the Path.home() leak. Both surfaced on full pytest pre-release gate, fixed in stage, ship in v0.51.19. Tests: full suite green. --- api/config.py | 24 ++++++++++++++++++++++++ tests/test_bootstrap_discover_agent.py | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/api/config.py b/api/config.py index fed092bb..4dc1345e 100644 --- a/api/config.py +++ b/api/config.py @@ -789,11 +789,34 @@ def _resolve_configured_provider_id( config_obj: dict | None = None, *, base_url: object = None, + resolve_alias: bool = True, ) -> str: + """Normalize a configured provider id. + + When ``resolve_alias`` is True (default, used for active-provider / + badge surfaces), falls through to ``_resolve_provider_alias`` after the + named-custom check. When False (used by ``resolve_model_provider``), + preserves the raw provider value so downstream local-server detection + (`_LOCAL_SERVER_PROVIDERS` membership in #1625) sees the original name + like ``ollama`` / ``lm-studio`` rather than alias-collapsed ``custom`` / + ``lmstudio``. The base-url-to-named-slug fallback still runs in both + modes when applicable. + + See in-stage absorption note on stage-313 for the #1625 regression that + motivated the ``resolve_alias`` flag. + """ named_slug = _named_custom_provider_slug_for_provider(provider, config_obj) if named_slug: return named_slug + if not resolve_alias: + raw = str(provider or "").strip().lower() + if base_url and raw == "custom": + by_base_url = _named_custom_provider_slug_for_base_url(base_url, config_obj) + if by_base_url: + return by_base_url + return str(provider or "") + resolved = _resolve_provider_alias(provider) if ( base_url @@ -1423,6 +1446,7 @@ def resolve_model_provider(model_id: str) -> tuple: model_cfg.get("provider"), cfg, base_url=config_base_url, + resolve_alias=False, ) # Heal legacy ``provider: local`` entries (written by WebUI < v0.50.252) diff --git a/tests/test_bootstrap_discover_agent.py b/tests/test_bootstrap_discover_agent.py index e6884a1e..c9592c9e 100644 --- a/tests/test_bootstrap_discover_agent.py +++ b/tests/test_bootstrap_discover_agent.py @@ -54,6 +54,13 @@ def _isolate_discover_agent_dir(monkeypatch, tmp_path, hermes_path): # Force REPO_ROOT.parent to a dir that won't accidentally contain a # `hermes-agent` sibling on the dev machine running these tests. monkeypatch.setattr(bootstrap, "REPO_ROOT", tmp_path / "isolated-repo-root") + # Pin Path.home() to a directory with no `.hermes/hermes-agent` or + # `hermes-agent` so the hard-coded `Path.home() / ".hermes" / "hermes-agent"` + # / `Path.home() / "hermes-agent"` candidates in `discover_agent_dir()` + # cannot pick up the dev machine's real install. Stage-313 absorbed + # this in-stage after the original test file isolated only env vars + # and REPO_ROOT, missing the Path.home() leakage. + monkeypatch.setattr(bootstrap.Path, "home", classmethod(lambda cls: tmp_path / "isolated-home")) def test_discovers_agent_dir_from_hermes_shebang(monkeypatch, tmp_path):