Skip to content

fix(provider): select anthropic base_url for dual-section named providers in sub-sessions (#11059) - #37705

Closed
rodboev wants to merge 3 commits into
NousResearch:mainfrom
rodboev:pr/named-provider-anthropic-base-url
Closed

fix(provider): select anthropic base_url for dual-section named providers in sub-sessions (#11059)#37705
rodboev wants to merge 3 commits into
NousResearch:mainfrom
rodboev:pr/named-provider-anthropic-base-url

Conversation

@rodboev

@rodboev rodboev commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Named custom providers with api_mode: anthropic_messages don't strip trailing /vN suffixes from base_url before handing it to the Anthropic SDK. The SDK appends its own /v1/messages, producing double-versioned paths like .../v4/v1/messages or .../v1/v1/messages that 404.

This bites cron and delegate sub-sessions that resolve a named provider carrying an OpenAI-wire base URL (e.g. https://gateway.example.com/v4) while the transport is anthropic_messages.

The azure-foundry branch (line 370) and opencode-zen/go branch (line 407) of _resolve_runtime_from_pool_entry already strip /v1 for anthropic_messages, but the named custom provider path (_resolve_named_custom_runtime) and the bare provider=custom fallback were both missing the same guard.

Repro findings

Root cause location: hermes_cli/runtime_provider.py, function _resolve_named_custom_runtime (lines 726-734 before fix).

Mechanism: The function correctly derives api_mode from the custom provider config or auto-detects it from the URL, but returns base_url as-is without any version-suffix normalization. When api_mode == "anthropic_messages", the Anthropic SDK appends /v1/messages to the base URL, creating path doubling.

Three code paths affected:

  1. Direct path (no pool): result["base_url"] was the raw config URL.
  2. Pool path: _try_resolve_from_custom_pool returns base_url unstripped; the caller didn't post-process it.
  3. Bare provider=custom fallback: both its pool result and direct return passed base_url through without stripping.

The cron scheduler (cron/scheduler.py:1565-1566) forwards job.get("base_url") as explicit_base_url. When the job lacks a base_url, resolution falls to the provider config, which hits the named custom provider path. No changes needed in cron/scheduler.py; the fix is entirely in the resolution function.

Changes

hermes_cli/runtime_provider.py:

  1. Direct path (after line 726): Extract api_mode into a variable before building the result dict. When api_mode == "anthropic_messages", apply re.sub(r"/v\d+/?$", "", base_url) to strip any trailing /vN suffix.

  2. Pool path (after line 696): After _try_resolve_from_custom_pool returns, apply the same /vN stripping to pool_result["base_url"] when the resolved api_mode is anthropic_messages.

The regex /v\d+/?$ is intentionally broader than the existing /v1/?$ pattern used in the azure-foundry branch, because named providers can carry arbitrary version suffixes (/v1, /v4, etc.) depending on the gateway configuration.

tests/hermes_cli/test_runtime_provider_resolution.py (7 new tests):

Test What it verifies
test_named_custom_provider_anthropic_strips_v1 /v1 stripped for anthropic_messages
test_named_custom_provider_anthropic_strips_v4 /v4 stripped for anthropic_messages
test_named_custom_provider_anthropic_no_strip_when_no_version_suffix URLs without /vN are untouched
test_named_custom_provider_chat_completions_keeps_v1 /v1 preserved for chat_completions
test_named_custom_provider_anthropic_pool_strips_v1 Pool path also strips /v1
test_named_custom_provider_anthropic_strips_v1_trailing_slash /v1/ (trailing slash) stripped

Test plan

  • All 7 new tests pass
  • All 133 existing tests in test_runtime_provider_resolution.py pass
  • Existing test_named_custom_provider_anthropic_api_mode still passes (its URL https://proxy.example.com/anthropic has no /vN suffix)
  • Manual verification with a dual-section gateway (if available)

Validation

Scenario Before After
Named provider, anthropic_messages, base_url .../v1 SDK → .../v1/v1/messages (404) SDK → .../v1/messages
Named provider, anthropic_messages, base_url .../v4 SDK → .../v4/v1/messages (404) SDK → .../v1/messages
Named provider, anthropic_messages, base_url .../anthropic Unchanged Unchanged
Named provider, chat_completions, base_url .../v1 Unchanged Unchanged
Pool path, anthropic_messages, base_url .../v1 SDK → .../v1/v1/messages (404) SDK → .../v1/messages

Closes #11059.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard provider/anthropic Anthropic native Messages API labels Jun 2, 2026
@rodboev
rodboev force-pushed the pr/named-provider-anthropic-base-url branch from 3f07ed8 to 255d158 Compare June 28, 2026 16:31
@rodboev
rodboev force-pushed the pr/named-provider-anthropic-base-url branch from d5b804d to 2ab861b Compare June 28, 2026 20:00
@rodboev

rodboev commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

Withdrawing this in favor of #45842, which already landed with the overlapping Anthropic base URL fix. Closing to keep the queue clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Cron sub-agent path collision: /v4/v1/messages (OpenAI + Anthropic URL concatenation)

2 participants