# NousResearch/hermes-agent · Auxiliary client resolution for named custom providers

## Open now

- 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.

## TL;DR

- 3 member(s): 0 open issue(s), 1 closed issue(s), 0 open PR(s), 1 merged PR(s), 1 PR(s) closed without merging.
- A fix was merged to main in PR #59301. Verify it addresses your case.
- **Recommended:** read PR #59301 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `unresolved`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_SPANNING`): every member is connected by structural evidence: shared changed files, or a closing/superseded reference.
- Pairwise link density `0.67`: the share of member 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.

## Closed / duplicate issues

- #45480: auxiliary auto-detect falls through to wrong provider when main provider is named custom (custom_providers/providers dict)

## Prior & competing PRs

- **#59301: fix(auxiliary): reuse main_runtime credentials for named custom providers**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`
  - files: `agent/auxiliary_client.py`, `tests/agent/test_auxiliary_named_custom_providers.py`
  - fix hunks:
  - `agent/auxiliary_client.py`

    ```diff
    @@ -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 = (
    ```

    ```diff
    @@ -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`

    ```diff
    @@ -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"
    ```
- **#45531: fix: custom provider auxiliary client resolution failures**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `5e116285465854b1d149247c84a4d2c2d031b386`, `92da7a9970548340f0d538ef94d7af543d1dc7a5`, `#59301`

## Fix status

- Merged to main in PR #59301 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-07-06`.
- Subsystem: `agent`
