fix(anthropic): override SDK User-Agent for third-party endpoints (#24293) - #24295
fix(anthropic): override SDK User-Agent for third-party endpoints (#24293)#24295briandevans wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the Anthropic client construction logic so that when Hermes targets third-party (non-anthropic.com) Anthropic-compatible endpoints, it overrides the Anthropic Python SDK’s default User-Agent with a Hermes-specific hermes-agent/<version> UA to avoid Cloudflare/WAF blocks, while keeping native anthropic.com traffic unchanged.
Changes:
- Add lazy Hermes version resolution and cache it for use in
User-Agentheaders. - Override
User-Agentfor third-party Anthropic endpoints inbuild_anthropic_client. - Extend and update unit tests to assert the new UA behavior for third-party endpoints and the absence of the override for native Anthropic.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
agent/anthropic_adapter.py |
Adds Hermes version resolver and sets default_headers["User-Agent"] for third-party endpoints while preserving native Anthropic behavior. |
tests/agent/test_anthropic_adapter.py |
Updates existing coverage and adds regression tests to validate UA override behavior and ensure native Anthropic requests aren’t modified. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
CI audit — all test failures on this PR are pre-existing baselines on clean Reproduced locally on
None of these touch |
cce2916 to
2ebba6e
Compare
eddceed to
93160ac
Compare
c1098da to
665dbe8
Compare
|
Closing to focus the queue on security/file-safety work where civilian merges are landing. Happy to reopen if maintainers want this picked up. |
|
Reopening — this is provider-SDK-drift hardening (third-party Anthropic-endpoint UA), which is a salvage niche worth keeping open. Apologies for the churn. |
|
Housekeeping: closing to keep my open-PR set focused on actively-reviewed work. This has been open ~20d without maintainer review and the surrounding code has continued to move, so it's unlikely to land as-is. The underlying fix still stands — happy to reopen and rebase if it would be useful. Thanks! |
|
Reopening — closed this in error during queue housekeeping. This is in an active salvage niche (security / provider-SDK-drift / install-correctness / civilian-repro'd P1), not stale plumbing. Keeping it open. |
665dbe8 to
ada252c
Compare
c40d2ca to
e44ce85
Compare
…usResearch#24293) Self-hosted Anthropic-compatible proxies (e.g. GLM relays, custom Cloudflare-fronted gateways) commonly enable bot-detection rules that 403 the Anthropic Python SDK's default ``Anthropic/Python <ver>`` User-Agent. The user-reported workaround was to manually patch ``build_anthropic_client`` to send a non-SDK UA in the third-party branch. This change applies that workaround unconditionally for any ``_is_third_party_anthropic_endpoint`` request, setting ``User-Agent: hermes-agent/<version>``. The pattern mirrors the existing OAuth (``claude-cli/...``) and Kimi-coding (``claude-code/0.1.0``) branches in the same function, which already override the SDK UA for routing reasons. Direct ``anthropic.com`` traffic is unaffected — those requests still ship with the SDK's native UA, which Anthropic's infrastructure relies on for routing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
e44ce85 to
1be2799
Compare
|
Closing to keep the queue focused — this has sat 47 days with no maintainer pickup. Happy to reopen if maintainers want the third-party-endpoint User-Agent override picked up. |
What does this PR do?
Override the Anthropic Python SDK's default
User-Agent: Anthropic/Python <ver>withUser-Agent: hermes-agent/<version>wheneverbuild_anthropic_clientconstructs a client for a third-party (non-anthropic.com) endpoint. Directanthropic.comtraffic is unchanged — the SDK's native UA is preserved there.Self-hosted Anthropic-compatible proxies (e.g. GLM relays, custom gateways) commonly sit behind Cloudflare with bot-detection rules that 403 the Anthropic SDK fingerprint. Reproduced in #24293:
In
agent/anthropic_adapter.py, the_is_third_party_anthropic_endpointbranch ofbuild_anthropic_clientonly setanthropic-betaindefault_headers; the SDK's default UA flowed through untouched, so every request from Hermes to such a proxy was rejected withHTTP 403: Your request was blocked.Pattern mirrors the existing OAuth (claude-cli/...) and Kimi-coding (claude-code/0.1.0) branches in the same function — both already override the SDK UA for routing reasons. Version resolution lazily importshermes_cli.__version__and caches it; falls back to"0.0.0"if unavailable (mirrorsacp_adapter/server.pyandgateway/platforms/yuanbao.pyconventions).Related Issue
Fixes #24293
Type of Change
Changes Made
agent/anthropic_adapter.py— in the third-party branch ofbuild_anthropic_client, populatedefault_headers["User-Agent"]withhermes-agent/<resolved-version>before constructing the SDK client. Native-Anthropic branch untouched.tests/agent/test_anthropic_adapter.py— new cases:test_third_party_endpoint_overrides_sdk_user_agentasserts UA starts withhermes-agent/andAnthropic/Pythonnever appears;test_native_anthropic_endpoint_does_not_override_user_agentasserts no UA override for directanthropic.comcalls. Updatestest_custom_base_urlto assert bothanthropic-betaand the newUser-Agentare present together.How to Test
uv run --with pytest --with pytest-xdist --with pytest-asyncio python3 -m pytest tests/agent/test_anthropic_adapter.py::TestBuildAnthropicClient tests/agent/test_anthropic_adapter.py::TestBuildAnthropicKwargs -vKeyError: 'User-Agent'. Restore — all 10TestBuildAnthropicClientcases pass.Checklist
Code
fix(scope):,feat(scope):, etc.)Documentation & Housekeeping
docs/, docstrings) — N/Acli-config.yaml.exampleif I added/changed config keys — N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/ARelated / Positioning
claude-cli/...) and Kimi-coding (claude-code/0.1.0) UA-override branches in the same function.custom_providers.user_agentconfig knob is deliberately not included here — the default-override pattern matches how the existing OAuth/Kimi branches handle SDK UA overrides and unblocks affected users without requiring per-provider config awareness. A config-knob follow-up can layer on top if maintainers want explicit per-provider control.