Skip to content

fix(auxiliary): prevent URL double-rewrite for custom anthropic_messages endpoints - #29624

Closed
yimi128 wants to merge 1 commit into
NousResearch:mainfrom
yimi128:fix/auxiliary-anthropic-custom-endpoint-404
Closed

fix(auxiliary): prevent URL double-rewrite for custom anthropic_messages endpoints#29624
yimi128 wants to merge 1 commit into
NousResearch:mainfrom
yimi128:fix/auxiliary-anthropic-custom-endpoint-404

Conversation

@yimi128

@yimi128 yimi128 commented May 21, 2026

Copy link
Copy Markdown

markdown
Bug Description

Auxiliary tasks (title generation, vision, compression, etc.) fail with HTTP 404 when using a custom provider with api_mode: anthropic_messages (e.g. Zhipu GLM via https://open.bigmodel.cn/api/anthropic).

Error:

Auxiliary title generation failed: HTTP 404: Error code: 404 - {'status': 404, 'error': 'Not Found', 'path': '/v4/v1/messages'}


Root Cause

In resolve_provider_client()'s custom + explicit_base_url branch, the URL passes through two contradictory rewrites:

1. _to_openai_base_url() rewrites /api/anthropic → /api/paas/v4 (OpenAI-wire format), because the auxiliary client historically uses the OpenAI SDK.
2. _maybe_wrap_anthropic() detects api_mode=anthropic_messages and builds an AnthropicAuxiliaryClient on the already-rewritten URL.
3. The Anthropic SDK appends /v1/messages to /api/paas/v4, producing /v4/v1/messages → 404.

The two steps assume mutually exclusive transports: step 1 assumes OpenAI SDK, step 2 assumes Anthropic SDK. When both fire, the URL is mangled.

Fix

When api_mode == "anthropic_messages", bypass _to_openai_base_url() entirely and build an AnthropicAuxiliaryClient directly from the original explicit_base_url (e.g. https://open.bigmodel.cn/api/anthropic). This mirrors the approach already used in _try_custom_endpoint() (the Step-2 fallback path), which correctly handles anthropic_messages by building the Anthropic client first.

On ImportError (anthropic SDK not installed) or other exceptions, falls back to the existing OpenAI-wire path.

How to Verify

1. Configure a custom provider with Anthropic-compatible endpoint:
   yaml
   model:
     default: glm-5.1
     provider: custom
     base_url: https://open.bigmodel.cn/api/anthropic
     api_mode: anthropic_messages
     api_key: <your-key>

2. Start a new session with hermes
3. Send any message — previously this would log Auxiliary title generation failed: HTTP 404
4. After the fix, title generation succeeds and the session gets a proper title
5. Verify with a unit test:
   python
   from agent.auxiliary_client import resolve_provider_client
   client, model = resolve_provider_client(
       'custom', 'glm-5.1',
       explicit_base_url='https://open.bigmodel.cn/api/anthropic',
       explicit_api_key='test-key',
       api_mode='anthropic_messages',
   )
   assert type(client).name == 'AnthropicAuxiliaryClient'
   assert str(getattr(client, 'base_url', '')).endswith('/api/anthropic')


Test Plan

- [x] Manual verification with Zhipu GLM Anthropic-compatible endpoint
- [x] Module import succeeds (import agent.auxiliary_client)
- [x] resolve_provider_client returns correct AnthropicAuxiliaryClient with unrewritten URL
- [ ] Existing tests still pass
- [ ] Regression test added

Risk Assessment

Low — The fix adds an early-return branch that only triggers when api_mode == "anthropic_messages". All other code paths (OpenAI-wire custom endpoints, named providers, auto-detection without explicit api_mode) are unchanged. The fallback on ImportError ensures no regression if the anthropic SDK is missing.

…ges endpoints

When a custom provider uses api_mode=anthropic_messages (e.g. Zhipu GLM
via /api/anthropic), the auxiliary client's auto-detect chain (Step 1)
passed the explicit_base_url through _to_openai_base_url(), which
rewrote /api/anthropic → /api/paas/v4.  Then _maybe_wrap_anthropic()
detected api_mode=anthropic_messages and built an Anthropic SDK client
on the already-rewritten URL.  The Anthropic SDK appended /v1/messages
to /api/paas/v4, producing /v4/v1/messages → HTTP 404.

The fix checks api_mode early: when anthropic_messages is declared,
build an AnthropicAuxiliaryClient directly from the original URL,
bypassing _to_openai_base_url entirely.  Falls back to the existing
OpenAI-wire path on ImportError or other failures.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint area/config Config system, migrations, profiles labels May 21, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for tracing this transport mismatch. The premise still holds on current main: agent/auxiliary_client.py:4636 rewrites explicit_base_url, then agent/auxiliary_client.py:4693 passes that rewritten value to _wrap_if_needed(), whose Anthropic route builds the native client from the supplied base URL (agent/auxiliary_client.py:4525-4527, 1564-1580).

Problems

  • The PR has no regression test for this exact resolve_provider_client(... explicit_base_url=..., api_mode="anthropic_messages") path. Existing coverage in tests/agent/test_auxiliary_client_anthropic_custom.py:35-89 covers _try_custom_endpoint, which already has a direct Anthropic branch.

Suggested changes

  • Port the narrow early Anthropic-client branch onto the current explicit custom-endpoint block at agent/auxiliary_client.py:4635-4695.
  • Add a mocked regression asserting that an explicit Kimi /coding or /anthropic URL is preserved in AnthropicAuxiliaryClient.base_url; retain an OpenAI-wire control case.

This is an automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 13, 2026
@teknium1

Copy link
Copy Markdown
Contributor

This bug is now fixed on main via #85466 (salvage of #64891). Credit correction: your PR was submitted May 21 — the EARLIEST fix for this branch, more than seven weeks before the version that merged. The pre-merge duplicate sweep missed it (phrasing variance), which is our miss, not yours.

The merged variant was picked on implementation grounds (it keeps the /v1 rewrite for the plain-OpenAI fallback client and shipped a regression test file), but your diagnosis of the double-rewrite → 404 was first and correct. Closing as resolved-on-main with first-submitter credit to you. Sorry for the long wait on this one, and thanks.

@teknium1 teknium1 closed this Aug 13, 2026
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/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants