Skip to content

fix(anthropic): send thinking=disabled explicitly on third-party endpoints (#15700) - #15712

Closed
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/anthropic-deepseek-thinking-disabled-15700
Closed

fix(anthropic): send thinking=disabled explicitly on third-party endpoints (#15700)#15712
briandevans wants to merge 2 commits into
NousResearch:mainfrom
briandevans:fix/anthropic-deepseek-thinking-disabled-15700

Conversation

@briandevans

Copy link
Copy Markdown
Contributor

Summary

  • Third-party Anthropic-compatible endpoints (e.g. DeepSeek /anthropic) default to thinking mode when the thinking parameter is absent; native Anthropic disables thinking by default
  • When reasoning_config.enabled = False, Hermes now sends thinking: {"type": "disabled"} for third-party endpoints so they don't default to thinking on
  • Kimi /coding and native Anthropic are unaffected

The bug

build_anthropic_kwargs sets thinking only when thinking is enabled. For native Anthropic that is correct — thinking is disabled by default. But DeepSeek's /anthropic endpoint treats the absent parameter as "thinking enabled", causing every request with enabled=False to fail with HTTP 400:

The content[].thinking in the thinking mode must be passed back to the API.

The fix

Added an elif branch inside the existing reasoning_config block at agent/anthropic_adapter.py:

elif reasoning_config.get("enabled") is False and _is_third_party_anthropic_endpoint(base_url):
    kwargs["thinking"] = {"type": "disabled"}

_is_third_party_anthropic_endpoint returns True for any non-anthropic.com base URL. Kimi /coding is already excluded by the outer not _is_kimi_coding guard.

Test plan

  • Before: 2 of 4 new tests fail (assert None == {'type': 'disabled'}) — regression guard confirmed
  • After: all 4 new tests pass
  • Regression guard: reverted fix → test_reasoning_disabled_third_party_sends_explicit_disabled and test_reasoning_disabled_third_party_any_provider both fail with AssertionError: assert None == {'type': 'disabled'}; restored → 0 failures
  • tests/agent/test_anthropic_adapter.py — 131 passed, 10 pre-existing baseline failures (unchanged)
  • tests/agent/test_kimi_coding_anthropic_thinking.py — 8 passed (unchanged)
  • Existing test_reasoning_disabled still passes (native Anthropic still omits the key)

New test cases:

  • test_reasoning_disabled_third_party_sends_explicit_disabled — DeepSeek endpoint gets {"type": "disabled"}
  • test_reasoning_disabled_third_party_any_provider — any non-Anthropic endpoint gets {"type": "disabled"}
  • test_reasoning_disabled_kimi_coding_omits_thinking — Kimi /coding gets neither enabled nor disabled
  • test_reasoning_none_third_party_omits_thinkingreasoning_config=None on third-party endpoint still omits thinking

Related

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings April 25, 2026 16:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ensures Anthropic-compatible third-party endpoints that default to “thinking enabled” when the thinking field is absent will instead receive an explicit thinking: {"type": "disabled"} when reasoning_config.enabled is set to False, while preserving native Anthropic and Kimi /coding behavior.

Changes:

  • Add a third-party-only branch to send thinking={"type":"disabled"} when reasoning is explicitly disabled.
  • Add regression tests covering native Anthropic omission, third-party explicit disable, Kimi /coding omission, and reasoning_config=None behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
agent/anthropic_adapter.py Adds explicit thinking disable for third-party Anthropic-compatible endpoints when reasoning_config.enabled is False.
tests/agent/test_anthropic_adapter.py Adds tests validating the new third-party explicit-disable behavior and guarding native/Kimi exceptions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1680 to +1685
elif reasoning_config.get("enabled") is False and _is_third_party_anthropic_endpoint(base_url):
# Third-party Anthropic-compatible endpoints (e.g. DeepSeek /anthropic)
# default to thinking mode when the `thinking` parameter is absent.
# Native Anthropic disables thinking by default, so the key is omitted
# there — but third-party implementations must be told explicitly.
kwargs["thinking"] = {"type": "disabled"}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid catch — addressed in 3a4ab0f074f083f897814c7bae7eb0ec2a64b92f.

Proxy-hostname substring misclassification ✅ — Replaced the raw "anthropic.com" in normalized substring check with a proper hostname comparison using urlparse. The function now extracts the hostname and checks host == "anthropic.com" or host.endswith(".anthropic.com"), so https://anthropic.com.proxy.company.com/anthropic is correctly identified as a third-party endpoint and receives thinking={"type":"disabled"}.

Regression test added ✅ — test_reasoning_disabled_proxy_hostname_containing_anthropic_com covers exactly this URL pattern; confirmed it fails with the old substring code and passes with the fix.

base_url="https://third-party.example.com/anthropic",
)
assert kwargs.get("thinking") == {"type": "disabled"}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 3a4ab0f074f083f897814c7bae7eb0ec2a64b92f — see reply above on the companion thread.

@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 provider/anthropic Anthropic native Messages API labels Apr 25, 2026
briandevans and others added 2 commits April 29, 2026 08:14
…oints (NousResearch#15700)

DeepSeek's Anthropic-compatible endpoint (`/anthropic`) defaults to thinking
mode when the `thinking` parameter is absent.  Native Anthropic disables
thinking by default, so the key has always been omitted there — but
third-party implementations that mirror the Anthropic protocol must be told
explicitly to disable it.

Without the explicit `{"type": "disabled"}`, DeepSeek rejects the request
with HTTP 400 "The content[].thinking in the thinking mode must be passed
back to the API."

The fix adds an `elif` branch inside the existing `reasoning_config` block:
when `enabled` is `False` and the base URL is a third-party endpoint (i.e.
not anthropic.com), set `thinking={"type": "disabled"}`.  Kimi /coding is
already excluded by the outer `not _is_kimi_coding` guard and remains
unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… _is_third_party_anthropic_endpoint

A proxy URL like `https://anthropic.com.proxy.company.com/anthropic`
contains `anthropic.com` as a substring in its full URL string, causing
the old `"anthropic.com" in normalized` check to misclassify it as the
native Anthropic API. This would suppress the `thinking={"type":"disabled"}`
parameter required for third-party DeepSeek-style endpoints.

Fix: parse the URL and compare the hostname against `anthropic.com` /
`*.anthropic.com` rather than doing a substring match on the raw string.

Adds a regression test for the proxy-hostname edge case.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@briandevans
briandevans force-pushed the fix/anthropic-deepseek-thinking-disabled-15700 branch from 3a4ab0f to 4d630ff Compare April 29, 2026 15:15
@briandevans

Copy link
Copy Markdown
Contributor Author

Rebased onto current origin/main (13c238327) — was 94h stale and the previous base predated the related Kimi thinking-suppression work (83c288da0 fix(anthropic): broaden Kimi thinking-suppression to custom endpoints (#17455)) and the dot-mangling narrowing (7141cda96).

Re-checked the interaction with that newer Kimi suppression — they're orthogonal:

  • #17455 short-circuits the entire if reasoning_config… block via _is_kimi_coding so Kimi /coding endpoints emit no thinking parameter at all (server-side enabled).
  • This PR adds an elif reasoning_config.get("enabled") is False and _is_third_party_anthropic_endpoint(base_url) branch that explicitly sends thinking={"type": "disabled"} for third-party Anthropic-compat endpoints (DeepSeek /anthropic, etc.) where the parameter defaults to enabled when absent.

Clean rebase, no conflicts. Re-ran focused tests on the rebased head (4d630ffd6):

  • tests/agent/test_anthropic_adapter.py -k "thinking or third_party or reasoning_disabled" — 20/20 pass (covers the new test_reasoning_disabled_third_party_sends_explicit_disabled, the existing native-Anthropic test_reasoning_disabled, the Kimi-coding test_reasoning_disabled_kimi_coding_omits_thinking, and the hostname-vs-substring guard test_reasoning_disabled_proxy_hostname_containing_anthropic_com).

The fix and its hostname-comparison hardening (substring anthropic.com would have false-matched proxies like anthropic.com.example.com) are unchanged from review.

@briandevans

Copy link
Copy Markdown
Contributor Author

Closing — issue #15700 was resolved by @teknium1's #26648 (fix(deepseek): wire thinking-mode via DeepSeekProfile), which closes #15700/#17212/#17825 from the DeepSeekProfile path instead of the adapter-level send-disabled approach taken here. That's the right place for the fix. Thanks for the merge!

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 P2 Medium — degraded but workaround exists 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.

DeepSeek API: Missing 'thinking: disabled' parameter causes 400 error

3 participants