Skip to content

fix(tools): dedup tool names at API boundary for Vertex/Azure/Bedrock (salvage #18532)#18748

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-3d89efe9
May 2, 2026
Merged

fix(tools): dedup tool names at API boundary for Vertex/Azure/Bedrock (salvage #18532)#18748
teknium1 merged 1 commit into
mainfrom
hermes/hermes-3d89efe9

Conversation

@teknium1

@teknium1 teknium1 commented May 2, 2026

Copy link
Copy Markdown
Contributor

Providers that enforce tool-name uniqueness (Google Vertex, Azure, Amazon Bedrock, Anthropic) now receive deduplicated tool lists even if an upstream injection path regresses. Before this change, a duplicate from any source (context-engine plugin re-registration, cache poisoning, re-init path) caused HTTP 400 Tool names must be unique — non-retryable and silent when fallback chains exhausted.

Cherry-picked from @liuhao1024's #18532 onto current main.

What changed

  • agent/auxiliary_client.py: _build_call_kwargs() dedups tools before kwargs["tools"] = … (covers all chat_completions providers).
  • agent/anthropic_adapter.py: convert_tools_to_anthropic() dedups in the loop (covers the native Anthropic Messages API path).
  • Dupes are dropped with logger.warning, first occurrence wins.
  • 8 new tests (4 per module) cover unique passthrough, dedup, empty, None.

Why this layer (and not just the root-cause fix)

The root-cause dedup in run_agent.py (context-engine + memory-tool injection) is already on main — this PR adds defensive guards at the two API-boundary functions so any future injection-path regression converts a hard 400 into a warning rather than silently exhausting the fallback chain. Intentionally conservative.

Validation

  • scripts/run_tests.sh tests/agent/test_auxiliary_client.py tests/agent/test_anthropic_adapter.py → 260 passed.
  • E2E: real _build_call_kwargs(provider="openrouter", …) and convert_tools_to_anthropic(…) called with a 7-tool list containing 2 duplicates (simulating the hermes-lcm plugin double-registration). Both paths return 5 unique tools, first-occurrence ordering preserved.

Closes #18478.

…edrock

Providers like Google Vertex, Azure, and Amazon Bedrock reject API
requests with duplicate tool names (HTTP 400: 'Tool names must be
unique').  The upstream injection paths in run_agent.py already dedup
after PR #17335, but two API-boundary functions pass tools through
without checking:

- agent/auxiliary_client.py: _build_call_kwargs() (all non-Anthropic
  providers in chat_completions mode)
- agent/anthropic_adapter.py: convert_tools_to_anthropic() (Anthropic
  Messages API path)

Add defensive dedup guards at both sites.  Duplicates are dropped with
a warning log, converting a hard 400 failure into a recoverable
condition.  This is intentionally conservative — the root-cause dedup
in run_agent.py is the primary defense; these guards add resilience
against future injection-path regressions.

Includes 8 new tests covering unique passthrough, duplicate removal,
empty/None edge cases.

Closes #18478
@teknium1 teknium1 merged commit 9bf2604 into main May 2, 2026
8 of 10 checks passed
@teknium1 teknium1 deleted the hermes/hermes-3d89efe9 branch May 2, 2026 08:51
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround comp/agent Core agent loop, run_agent.py, prompt builder comp/tools Tool registry, model_tools, toolsets labels May 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent loop, run_agent.py, prompt builder comp/tools Tool registry, model_tools, toolsets P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Duplicate tool names cause silent 400 failures on strict providers (Google Vertex, Azure, Bedrock) after 4d363499d

3 participants