feat(agent): add model.preserve_dots opt-in override for Anthropic-compatible proxies#13890
Open
Chao1208 wants to merge 1 commit into
Open
feat(agent): add model.preserve_dots opt-in override for Anthropic-compatible proxies#13890Chao1208 wants to merge 1 commit into
Chao1208 wants to merge 1 commit into
Conversation
Add an explicit ``model.preserve_dots: true`` config flag that forces ``_anthropic_preserve_dots`` to return the configured value regardless of the provider/base_url allowlist. This lets users point Hermes at third-party Anthropic-compatible proxies (OneAPI, LiteLLM, FastGPT, company-internal gateways, etc.) whose registered model IDs contain dots — e.g. ``Claude Opus 4.6`` routed to AWS Bedrock — without having to hardcode every new proxy URL into Hermes itself. Motivation When pointing Hermes at a corporate OneAPI gateway configured with ``model: Claude Opus 4.6`` and ``api_mode: anthropic_messages``, the outgoing request body contained ``"model": "Claude Opus 4-6"`` because ``normalize_model_name`` strips dots by default (OpenRouter convention). The gateway responds with HTTP 503 "no available channel for model claude opus 4-6" because its catalog only knows the dotted form. The existing allowlist in ``_anthropic_preserve_dots`` handles Alibaba/DashScope, MiniMax, OpenCode, ZAI, Bedrock — but every third-party Anthropic-compatible proxy would need its own entry, which doesn't scale. Behavior - ``preserve_dots: true`` → always preserve dots - ``preserve_dots: false`` → always mangle dots (escape hatch) - missing / non-bool → fall back to existing allowlist (no behavior change for users who don't opt in) The override is read once in ``__init__`` and cached on the instance, so existing unit tests that construct ``AIAgent`` via ``SimpleNamespace`` are unaffected. Test plan - New test file ``tests/agent/test_preserve_dots_override.py`` with 8 cases covering True/False/missing/None/non-bool and interactions with the existing allowlist. - Existing bedrock/anthropic/minimax preserve-dots tests still pass (208/208, no regressions). - End-to-end verified against a real OneAPI gateway with ``model.default: Claude Opus 4.6`` — request body now carries the dotted model ID and the gateway routes successfully. Tested on: macOS 14 (darwin 23.2.0), Python 3.11.15. Made-with: Cursor
Author
|
Friendly ping @teknium1 — opened this 6 days ago. The change is small and additive (an opt-in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an explicit
model.preserve_dots: trueconfig flag that forcesAIAgent._anthropic_preserve_dots()to return the configured value,overriding the provider/base-URL allowlist. This lets users point
Hermes at third-party Anthropic-compatible proxies — OneAPI, LiteLLM,
FastGPT, company-internal gateways, etc. — whose registered model IDs
contain dots, without requiring Hermes to hardcode every new proxy URL.
Why
The default behaviour of
normalize_model_nameconverts dots tohyphens (
claude-opus-4.6→claude-opus-4-6) to match theAnthropic/OpenRouter convention.
_anthropic_preserve_dotsoptsspecific providers (
alibaba,minimax,opencode-go,opencode-zen,zai,bedrock) and base-URL substrings (dashscope,aliyuncs,minimax,opencode.ai/zen/,bigmodel.cn,bedrock-runtime.) outof this mangling.
That allowlist doesn't scale. Any corporate or third-party Anthropic-
compatible gateway that registers a dotted model ID silently breaks:
…even though the operator registered the channel as
Claude Opus 4.6.Reproducer: point Hermes at a OneAPI-style proxy with:
…and watch the request body go out as
"model": "Claude Opus 4-6".The proxy can't match that against its catalog and returns 503.
Rather than keep adding one-off URL substrings to the allowlist, this
PR exposes a single opt-in knob. Users who know their proxy needs
dotted model IDs can add one line to
~/.hermes/config.yaml:…and every future proxy in this class is handled without a Hermes
release.
Behaviour matrix
model.preserve_dotstruefalseThe override is read once in
AIAgent.__init__and cached on theinstance. Existing unit tests that construct lightweight agents via
SimpleNamespacedon't set the attribute and therefore land on thefallback path, i.e. this is fully backwards compatible.
How to test
Automated
Eight new cases covering:
preserve_dots: trueon a URL not in the allowlist (core case)preserve_dots: trueoverridingprovider=anthropicpreserve_dots: falseoverridingprovider=bedrockpreserve_dots: falseoverridingdashscope.aliyuncs.comURLNone/ non-bool attribute values → fall back to allowlistRelated regression suites —
tests/agent/test_bedrock_integration.py,tests/agent/test_anthropic_adapter.py,tests/agent/test_minimax_provider.py— all still pass (208/208).Manual
Against a real OneAPI-style proxy that registers
Claude Opus 4.6:Configure
~/.hermes/config.yaml:hermes chat -q "say hi in exactly 3 words"→ returns a normalassistant message. Without
preserve_dots: truethe proxyresponds with
HTTP 503 no available channel for claude opus 4-6.Inspect
~/.hermes/sessions/request_dump_*.jsonand confirmrequest.body.modelis the dotted"Claude Opus 4.6".Platforms tested
No platform-specific code paths are touched — the change is pure
Python config reading.
Risks
Minimal:
identical behaviour).
__init__, sohand-edited YAML like
preserve_dots: "yes"can't silentlyflip the flag.
dict.get()in the hot path of_anthropic_preserve_dots.Related
the opencode-go fix (Bug: OpenCode Go model names with dots get hyphenated, causing HTTP 401 (minimax-m2.7, glm-4.5, kimi-k2.5) #5211, commit f77be22), now user-extensible
without code changes.