hermes - ✅(Solved) Fix [Bug]: named custom providers under providers: ignore explicit api_mode during runtime resolution [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Named custom providers defined under providers: drop the explicit api_mode during runtime resolution.

As a result, a provider that is configured with a non-default protocol (for example codex_responses) can resolve correctly when used through the legacy model.provider: custom path, but resolve incorrectly when selected by its named provider key.

Root Cause

Suspected root cause

hermes_cli/runtime_provider.py:

Fix Action

Fixed

PR fix notes

PR #13087: fix(cli): preserve api_mode for named custom providers in runtime resolution (#13051)

Description (problem / solution / changelog)

Problem

Named custom providers defined under providers: drop the explicit api_mode during runtime resolution, so an endpoint configured with a non-default protocol (for example codex_responses) resolves correctly through the legacy model.provider: custom path but falls back to chat_completions when selected by its provider key. Cause: _get_named_custom_provider() did not copy entry.get('api_mode') into the dict it returned for the providers: branches (legacy custom_providers: list path at lines 377-379 already did this).

Fixes #13051.

Fix

Parse entry.get('api_mode') via _parse_api_mode once per providers: entry, and include it in both the provider-key-match and display-name-match return branches. _resolve_named_custom_runtime already calls custom_provider.get('api_mode') first, so no downstream change is needed — the named path now mirrors the legacy path.

Test plan

  • Added test_named_custom_provider_explicit_api_mode_from_providers_dict — uses an unknown host (codex.example.invalid) so _detect_api_mode_for_url returns None, proving the explicit api_mode is the only source of truth on the fixed path.
  • pytest tests/hermes_cli/test_runtime_provider_resolution.py — 68 passed
  • Reran the issue's resolve_runtime_provider(requested='<named>') call on the fixed branch: api_mode now resolves to codex_responses, matching the legacy requested='custom' call.

Changed files

  • hermes_cli/runtime_provider.py (modified, +12/-2)
  • tests/hermes_cli/test_runtime_provider_resolution.py (modified, +40/-0)

Code Example

model:
  provider: custom
  base_url: https://<redacted-endpoint>/v1
  api_mode: codex_responses
  default: <redacted-model>

providers:
  <redacted-provider-name>:
    name: <redacted-display-name>
    base_url: https://<redacted-endpoint>/v1
    api_key: <redacted>
    default_model: <redacted-model>
    api_mode: codex_responses
    models:
      <redacted-model>: {}

---

resolve_runtime_provider(requested='custom')
# => {
#      'provider': 'custom',
#      'base_url': 'https://<redacted-endpoint>/v1',
#      'api_mode': 'codex_responses',
#      ...
#    }

resolve_runtime_provider(requested='<redacted-provider-name>')
# => {
#      'provider': 'custom',
#      'base_url': 'https://<redacted-endpoint>/v1',
#      'api_mode': 'chat_completions',
#      ...
#    }

---

_detect_api_mode_for_url(base_url) or "chat_completions"

---

if requested_norm in {ep_name, name_norm, f"custom:{name_norm}"}:
    base_url = entry.get("api") or entry.get("url") or entry.get("base_url") or ""
    if base_url:
        return {
            "name": entry.get("name", ep_name),
            "base_url": base_url.strip(),
            "api_key": resolved_api_key,
            "model": entry.get("default_model", ""),
        }
RAW_BUFFERClick to expand / collapse

Summary

Named custom providers defined under providers: drop the explicit api_mode during runtime resolution.

As a result, a provider that is configured with a non-default protocol (for example codex_responses) can resolve correctly when used through the legacy model.provider: custom path, but resolve incorrectly when selected by its named provider key.

Impact

This makes the new-style providers: config unsafe for some custom endpoints.

In my case, the same endpoint works through the legacy path, but the named-provider path falls back to URL detection and ends up using chat_completions instead of the configured protocol.

Minimal redacted repro

Config:

model:
  provider: custom
  base_url: https://<redacted-endpoint>/v1
  api_mode: codex_responses
  default: <redacted-model>

providers:
  <redacted-provider-name>:
    name: <redacted-display-name>
    base_url: https://<redacted-endpoint>/v1
    api_key: <redacted>
    default_model: <redacted-model>
    api_mode: codex_responses
    models:
      <redacted-model>: {}

Runtime behavior observed locally:

resolve_runtime_provider(requested='custom')
# => {
#      'provider': 'custom',
#      'base_url': 'https://<redacted-endpoint>/v1',
#      'api_mode': 'codex_responses',
#      ...
#    }

resolve_runtime_provider(requested='<redacted-provider-name>')
# => {
#      'provider': 'custom',
#      'base_url': 'https://<redacted-endpoint>/v1',
#      'api_mode': 'chat_completions',
#      ...
#    }

This reproduces even when providers.<name>.api_mode is explicitly set.

Suspected root cause

hermes_cli/runtime_provider.py:

  • _get_named_custom_provider() returns name, base_url, api_key, and model
  • but it does not return api_mode
  • later _try_resolve_from_custom_pool(..., api_mode_override=None) falls back to:
_detect_api_mode_for_url(base_url) or "chat_completions"

Relevant current code:

if requested_norm in {ep_name, name_norm, f"custom:{name_norm}"}:
    base_url = entry.get("api") or entry.get("url") or entry.get("base_url") or ""
    if base_url:
        return {
            "name": entry.get("name", ep_name),
            "base_url": base_url.strip(),
            "api_key": resolved_api_key,
            "model": entry.get("default_model", ""),
        }

Expected behavior

If a named custom provider in providers: specifies api_mode, runtime resolution should preserve and use it.

In other words, the named-provider path should be behaviorally equivalent to the legacy custom-provider path for protocol selection.

Suggested fix

Include api_mode in _get_named_custom_provider() return data for providers: entries, and pass it through the same way other explicit runtime overrides are handled.

It would also be great to add a regression test covering:

  • providers: named custom provider with explicit api_mode
  • resolve_runtime_provider(requested='<name>')
  • expected api_mode == configured value

Notes

I searched existing issues and found related but distinct reports around custom-provider protocol selection / codex behavior, but not this specific named-provider regression.

extent analysis

TL;DR

The most likely fix is to modify the _get_named_custom_provider() function to include api_mode in its return data for providers: entries.

Guidance

  • Review the _get_named_custom_provider() function in hermes_cli/runtime_provider.py to ensure it returns api_mode along with other provider details.
  • Update the function to pass the api_mode value to _try_resolve_from_custom_pool() to prevent fallback to default chat_completions mode.
  • Verify that the api_mode is correctly preserved and used during runtime resolution for named custom providers.
  • Consider adding a regression test to cover the scenario with a named custom provider and explicit api_mode configuration.

Example

def _get_named_custom_provider(entry):
    # ... existing code ...
    return {
        "name": entry.get("name", ep_name),
        "base_url": base_url.strip(),
        "api_key": resolved_api_key,
        "model": entry.get("default_model", ""),
        "api_mode": entry.get("api_mode"),  # Add this line
    }

Notes

The suggested fix assumes that the api_mode value is correctly configured in the providers: entry. Additional testing and verification may be necessary to ensure the fix works as expected.

Recommendation

Apply the suggested fix to include api_mode in the _get_named_custom_provider() return data, as it directly addresses the identified issue and ensures consistent behavior between legacy and named-provider paths.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

If a named custom provider in providers: specifies api_mode, runtime resolution should preserve and use it.

In other words, the named-provider path should be behaviorally equivalent to the legacy custom-provider path for protocol selection.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING