Skip to content

fix(runtime): strip trailing /v1 from anthropic_messages custom provider base_url#44195

Open
AIalliAI wants to merge 1 commit into
NousResearch:mainfrom
AIalliAI:fix/44181-custom-provider-anthropic-v1-strip
Open

fix(runtime): strip trailing /v1 from anthropic_messages custom provider base_url#44195
AIalliAI wants to merge 1 commit into
NousResearch:mainfrom
AIalliAI:fix/44181-custom-provider-anthropic-v1-strip

Conversation

@AIalliAI

Copy link
Copy Markdown
Contributor

Problem

A custom_providers / providers: entry with api_mode: anthropic_messages and a base URL ending in /v1 (the natural choice when several entries share one multi-protocol gateway) fails silently: the Anthropic SDK appends /v1/messages to the base_url itself, so requests go to http://host:3001/v1/v1/messages — a 404 that surfaces only as an opaque auth / model-detection failure.

The opencode-zen/go resolver (runtime_provider.py ~L406) and the azure-foundry resolver (~L369, ~L1001) already strip the trailing /v1 for exactly this reason — custom providers were the remaining gap.

Fix

Adds a _strip_anthropic_v1_suffix() helper and applies it on the three custom-provider return paths where api_mode can be explicitly configured:

  • named custom_providers / providers: entries (_resolve_named_custom_runtime)
  • the custom credential-pool path (_try_resolve_from_custom_pool)
  • bare provider: custom with model.api_mode (openrouter/custom fallback resolver)

The strip is gated on api_mode == "anthropic_messages" — OpenAI-wire entries keep their /v1 untouched. A debug log records the rewrite. This is Option B from the issue (auto-normalize), matching the existing in-repo precedent rather than a docs-only fix.

Also documents the behavior in the custom-providers section of website/docs/integrations/providers.md.

Note on the issue's repro: the reported config uses a protocol: key, which isn't part of the schema (the recognized field is api_mode / transport; unknown keys are ignored with a logged warning). The underlying double-/v1 failure mode is real for api_mode: anthropic_messages, which this PR fixes.

Tests

4 new regression tests in tests/hermes_cli/test_runtime_provider_resolution.py (named entry strips, chat_completions keeps /v1, bare-custom strips, pool path strips). Full file: 133 passed.

Fixes #44181

🤖 Generated with Claude Code

…der base_url

The Anthropic SDK appends /v1/messages to base_url itself, so a custom
provider configured with api_mode: anthropic_messages and a gateway URL
like http://host:3001/v1 sent requests to /v1/v1/messages — a 404 that
surfaced only as an opaque auth/model-detection failure.

The opencode-zen/go and azure-foundry resolvers already normalise this
exact case; this extends the same strip to the three custom-provider
return paths (named custom_providers/providers entries, the custom
credential-pool path, and bare provider: custom with model.api_mode),
so entries sharing a multi-protocol gateway can all use the same /v1
base_url.

Fixes NousResearch#44181

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard area/config Config system, migrations, profiles labels Jun 11, 2026
@liuhao1024

Copy link
Copy Markdown
Contributor

✅ Verified — anthropic_messages /v1 suffix normalization

Reviewed the diff for correctness across all three custom-provider return paths.

  • Regex safety: re.sub(r"/v1/?$", "", base_url) correctly matches /v1 or /v1/ at end-of-string only — no false positives on paths like /apiv1 or /v1/custom/v2. Gate on api_mode == "anthropic_messages" prevents unintended stripping on OpenAI-wire entries.
  • Coverage: Applied in _resolve_named_custom_runtime, _try_resolve_from_custom_pool, and _resolve_openrouter_runtime (bare-custom fallback) — all three code paths where api_mode can be explicitly configured. No missed call sites.
  • No-op on empty/missing: if not base_url: return base_url handles the empty-string edge case; the regex no-ops when no /v1 suffix is present.
  • Test coverage: 4 regression tests covering named entry strip, chat_completions no-strip, bare-custom strip, and pool-path strip. All exercised through real resolve_runtime_provider or _try_resolve_from_custom_pool paths.

Clean, well-scoped fix. No issues found.

@AIalliAI

Copy link
Copy Markdown
Contributor Author

Requesting maintainer review — this is ready to land from my side. Standalone fork CI is pending first-run approval here; the rollup branch in #44061 carrying this session's batch is fully green on upstream CI (all test shards, typecheck, e2e).

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

Labels

area/config Config system, migrations, profiles comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

custom_providers: inconsistent base_url paths across protocols cause silent auth failures

3 participants