Skip to content

fix(anthropic): auto-detect Bedrock model IDs in normalize_model_name (#12295) - #14664

Closed
qike-ms wants to merge 1 commit into
NousResearch:mainfrom
qike-ms:fix/bedrock-model-id-dots
Closed

fix(anthropic): auto-detect Bedrock model IDs in normalize_model_name (#12295)#14664
qike-ms wants to merge 1 commit into
NousResearch:mainfrom
qike-ms:fix/bedrock-model-id-dots

Conversation

@qike-ms

@qike-ms qike-ms commented Apr 23, 2026

Copy link
Copy Markdown

What does this PR do?

normalize_model_name() unconditionally converts dots to hyphens, mangling Bedrock model IDs like anthropic.claude-opus-4-7 into anthropic-claude-opus-4-7. Bedrock rejects these with HTTP 400/404.

The existing mitigation (_anthropic_preserve_dots() in run_agent.py) only covers the main agent loop. All auxiliary client calls (compression, session_search, vision, flush_memories, title_generation) go through _AnthropicCompletionsAdapter which calls build_anthropic_kwargs() without preserve_dots=True. These calls have been silently broken for Bedrock users -- context compression fails, session search fails, etc.

This fix puts the detection inside normalize_model_name() itself so it covers all call sites without requiring callers to know about Bedrock.

Why fix it here instead of threading preserve_dots through every call site?

There are 10+ open PRs for #12295, all modifying _anthropic_preserve_dots() in run_agent.py. That approach:

Fixing at the normalize_model_name level is defense-in-depth: Bedrock IDs are structurally recognizable and should never have their dots mangled regardless of how the caller is configured.

Related Issue

Fixes #12295

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Changes Made

  • agent/anthropic_adapter.py: Added _is_bedrock_model_id() helper that detects Bedrock namespace prefixes (anthropic., us., eu., ap., jp., global.). normalize_model_name() now skips dot-to-hyphen conversion for detected Bedrock IDs.
  • tests/agent/test_bedrock_integration.py: Updated two canary tests that asserted the broken behavior. Added TestBedrockModelIdDetection class with 10 tests covering bare IDs, regional prefixes, negative cases, and end-to-end build_anthropic_kwargs verification.

How to Test

  1. python -m pytest tests/agent/test_bedrock_integration.py tests/agent/test_anthropic_adapter.py -x -q -- 186 passed
  2. python -m pytest tests/hermes_cli/test_model_normalize.py -x -q -- 55 passed
  3. Verify manually:
from agent.anthropic_adapter import normalize_model_name
# Bedrock IDs preserved (was broken):
assert normalize_model_name("anthropic.claude-opus-4-7") == "anthropic.claude-opus-4-7"
assert normalize_model_name("us.anthropic.claude-sonnet-4-5-v1:0") == "us.anthropic.claude-sonnet-4-5-v1:0"
# OpenRouter dots still converted (unchanged):
assert normalize_model_name("claude-opus-4.6") == "claude-opus-4-6"

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes
  • I've tested on my platform: Ubuntu 24.04, Linux 6.x, Python 3.11

Documentation & Housekeeping

  • I've updated relevant documentation (docstrings on normalize_model_name and new _is_bedrock_model_id) -- or N/A
  • N/A -- no config keys changed
  • N/A -- no architecture changes
  • N/A -- pure Python, no platform-specific code
  • N/A -- no tool schemas changed

…#12295)

Bedrock model IDs use dots as namespace separators (anthropic.claude-opus-4-7,
us.anthropic.claude-sonnet-4-5-v1:0), not version separators.
normalize_model_name() was unconditionally converting all dots to hyphens,
producing invalid IDs that Bedrock rejects with HTTP 400/404.

This affected both the main agent loop (partially mitigated by
_anthropic_preserve_dots in run_agent.py) and all auxiliary client calls
(compression, session_search, vision, etc.) which go through
_AnthropicCompletionsAdapter and never pass preserve_dots=True.

Fix: add _is_bedrock_model_id() to detect Bedrock namespace prefixes
(anthropic., us., eu., ap., jp., global.) and skip dot-to-hyphen
conversion for these IDs regardless of the preserve_dots flag.
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/anthropic Anthropic native Messages API labels Apr 23, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Competing fix for #12295 — differs from #12336/#12778/#13112 by moving Bedrock detection into normalize_model_name() instead of threading preserve_dots through call sites. Covers auxiliary clients (compression, session_search, vision) that other PRs miss.

teknium1 pushed a commit that referenced this pull request Apr 24, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of #14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by #14664,
and the aux client aws_sdk branch was subsumed by #14770 (both in the same
salvage PR as this commit).
justrhoto pushed a commit to justrhoto/hermes-agent that referenced this pull request Apr 24, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
@teknium1

Copy link
Copy Markdown
Contributor

This fix has already landed on main — closing as implemented.

Automated hermes-sweeper review:

  • Commit f2fba4f9a merged this exact change: _is_bedrock_model_id() helper + guarded normalize_model_name().
  • agent/anthropic_adapter.py lines 989–1031 now contain the Bedrock detection logic and the early-return guard, matching the PR's stated changes exactly.
  • The fix is on main but post-dates tag v2026.4.23; it will ship in the next release.

Thanks @qike-ms for the well-scoped fix and thorough test coverage — the approach of centralising detection inside normalize_model_name() rather than threading preserve_dots through every call site was the right call.

@teknium1 teknium1 closed this Apr 27, 2026
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
donald131 pushed a commit to donald131/hermes-agent that referenced this pull request May 2, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…rupt

Three interrupt-recovery sites in run_agent.py rebuilt self._anthropic_client
with build_anthropic_client(self._anthropic_api_key, ...) unconditionally.
When provider=bedrock + api_mode=anthropic_messages (AnthropicBedrock SDK
path), self._anthropic_api_key is the sentinel 'aws-sdk' — build_anthropic_client
doesn't accept that and the rebuild either crashed or produced a non-functional
client.

Extract a _rebuild_anthropic_client() helper that dispatches to
build_anthropic_bedrock_client(region) when provider='bedrock', falling back
to build_anthropic_client() for native Anthropic and other anthropic_messages
providers (MiniMax, Kimi, Alibaba, etc.). Three inline rebuild sites now call
the helper.

Partial salvage of NousResearch#14680 by @bsgdigital — only the _rebuild_anthropic_client
helper. The normalize_model_name Bedrock-prefix piece was subsumed by NousResearch#14664,
and the aux client aws_sdk branch was subsumed by NousResearch#14770 (both in the same
salvage PR as this commit).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround 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.

Bedrock Claude inference-profile IDs get dot-collapsed to hyphens -> BadRequestError 400

3 participants