NousResearch/hermes-agent

Auxiliary client resolution for named custom providers

Cluster 45480 · agent. Data as of

Nothing open on GitHub. Fix merged in #59301 on 6 Jul 2026. Check it covers your case.

The short answer

0open issues
0open PRs
3items grouped
Known?
Yes. 3 related items grouped here, 1 issue already closed.
Fix event
A fix was merged to main in PR #59301. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this unresolved. That is separate from the fix event.
Still open
Nothing open on GitHub.

All three members concern how the auxiliary client resolves or selects a provider when the main provider is a named custom one (e.g. defined in custom_providers/providers). One describes auto-detect landing on the wrong provider, and the other two appear to propose fixes involving resolution failures and reusing main runtime credentials; their outcomes are not stated in the titles.

Open issues0

No open issues.

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #59301 fix(auxiliary): reuse main_runtime credentials for named custom providers

    merged to main

    files: agent/auxiliary_client.py, tests/agent/test_auxiliary_named_custom_providers.py

    Show the diff
    • agent/auxiliary_client.py
      @@ -4307,6 +4307,8 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "",
       
           # ── Custom endpoint (OPENAI_BASE_URL + OPENAI_API_KEY) ───────────
           if provider == "custom":
      +        custom_base = ""
      +        custom_key = ""
               if explicit_base_url:
                   custom_base = _to_openai_base_url(explicit_base_url).strip()
                   custom_key = (
      @@ -4321,6 +4323,19 @@ def _wrap_if_needed(client_obj, final_model_str: str, base_url_str: str = "",
                           "but base_url is empty"
                       )
                       return None, None
      +        elif main_runtime:
      +            # When main_runtime carries a concrete base_url + api_key for a
      +            # named custom provider (custom:<name>), use it directly instead
      +            # of re-resolving from the bare "custom" provider name.
      +            # Re-resolution loses the provider name and falls back to
      +            # OpenRouter or a wrong API-key provider — the main agent already
      +            # solved this, we just need to reuse its answer. (#45472)
      +            _main_base = str(main_runtime.get("base_url") or "").strip().rstrip("/")
      +            _main_key = str(main_runtime.get("api_key") or "").strip()
      +            if _main_base and _main_key:
      +                custom_base = _main_base
      +                custom_key = _main_key
      +        if custom_base and custom_key:
                   final_model = _normalize_resolved_model(
                       model or (main_runtime.get("model") if main_runtime else None) or "gpt-4o-mini",
                       provider,
    • tests/agent/test_auxiliary_named_custom_providers.py
      @@ -491,3 +491,73 @@ def test_explicit_overrides_applied_on_api_key_branch(self, tmp_path, monkeypatc
               assert isinstance(client, OpenAI)
               assert "override.example.com" in str(client.base_url)
               assert client.api_key == "override-key"
      +
      +
      +class TestResolveProviderClientMainRuntimeCustom:
      +    """When the main agent uses a named custom provider (custom:<name>),
      +    resolve_provider_client('custom', ..., main_runtime=...) must reuse the
      +    main_runtime's base_url + api_key instead of re-resolving from the bare
      +    'custom' provider name.  Re-resolution loses the provider name and falls
      +    back to OpenRouter or a wrong API-key provider. (#45472)"""
      +
      +    def test_custom_provider_main_runtime_used_directly(self, tmp_path, monkeypatch):
      +        """main_runtime with base_url + api_key for a named custom provider
      +        is used directly, bypassing the _try_custom_endpoint / API-key
      +        fallback chain."""
      +        from agent.auxiliary_client import resolve_provider_client
      +        main_runtime = {
      +            "provider": "custom",
      +            "base_url": "https://my-gateway.example.com/v1",
      +            "api_key": "***",
      +            "model": "glm-5.1",
      +        }
      +        client, model = resolve_provider_client(
      +            "custom",
      +            model="explicit-glm-5.1",
      +            main_runtime=main_runtime,
      +        )
      +        assert client is not None
      +        assert model == "explicit-glm-5.1"
      +        assert "my-gateway.example.com" in str(client.base_url)
      +        assert client.api_key == "***"
      +
      +    def test_custom_provider_main_runtime_no_credentials_falls_through(self, tmp_path, monkeypatch):
      +        """When main_runtime has no base_url or no api_key, the existing
      +        _try_custom_endpoint / _resolve_api_key_provider fallback chain is
      +        still tried."""
      +        # Ensure no env-provided credentials interfere
      +        monkeypatch.delenv("OPENAI_API_KEY", raising=False)
      +        monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
      +        monkeypatch.delenv("OPENROUTER_API_KEY", raising=False)
      +        monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
      +
      +        from agent.auxiliary_client import resolve_provider_client
      +        # main_runtime with key but no base_url → must fall through
      +        client, model = resolve_provider_client(
      +            "custom",
      +            main_runtime={"api_key": "k", "base_url": ""},
      +        )
      +        # Should fall through to _try_custom_endpoint → return None,None
      +        # because no OPENAI_BASE_URL is set and no custom endpoint is configured
      +        assert client is None
      +
      +    def test_custom_provider_main_runtime_respects_explicit_base_url(self, tmp_path):
      +        """explicit_base_url still wins over main_runtime — the caller's
      +        explicit argument is the strongest signal."""
      +        from agent.auxiliary_client import resolve_provider_client
      +        main_runtime = {
      +            "base_url": "https://main-runtime.example.com/v1",
      +            "api_key": "[redacted:secret-assignment]",
      +            "model": "ignored-model",
      +        }
      +        client, model = resolve_provider_client(
      +            "custom",
      +            model="explicit-model",
      +            explicit_base_url="https://explicit.example.com/v1",
      +            explicit_api_key="[redacted:secret-assignment]",
      +            main_runtime=main_runtime,
      +        )
      +        assert client is not None
      +        assert model == "explicit-model"
      +        assert "explicit.example.com" in str(client.base_url)
      +        assert client.api_key == "sk-explicit"

1 PR closed without merging

From the close comments: merged via another change 1

  • #45531 fix: custom provider auxiliary client resolution failures · merged via another change → 5e11628, 92da7a9, #59301
Closed issues · 1
  • Closed#45480auxiliary auto-detect falls through to wrong provider when main provider is named custom (custom_providers/providers dict)
How this cluster was grouped
  • Link evidence (structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
  • Pairwise link density 0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
agent
Merged PRs
#59301
Closed issues
1
Attempts
1 PR closed without merging
Fix link checked
6 Jul 2026

For your agent

The same facts, typed.