fix(anthropic): send thinking=disabled explicitly on third-party endpoints (#15700) - #15712
Conversation
There was a problem hiding this comment.
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
/codingomission, andreasoning_config=Nonebehavior.
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.
| 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"} |
There was a problem hiding this comment.
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"} | ||
|
|
There was a problem hiding this comment.
Addressed in 3a4ab0f074f083f897814c7bae7eb0ec2a64b92f — see reply above on the companion thread.
…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>
3a4ab0f to
4d630ff
Compare
|
Rebased onto current Re-checked the interaction with that newer Kimi suppression — they're orthogonal:
Clean rebase, no conflicts. Re-ran focused tests on the rebased head (
The fix and its hostname-comparison hardening (substring |
|
Closing — issue #15700 was resolved by @teknium1's #26648 ( |
Summary
/anthropic) default to thinking mode when thethinkingparameter is absent; native Anthropic disables thinking by defaultreasoning_config.enabled = False, Hermes now sendsthinking: {"type": "disabled"}for third-party endpoints so they don't default to thinking on/codingand native Anthropic are unaffectedThe bug
build_anthropic_kwargssetsthinkingonly when thinking is enabled. For native Anthropic that is correct — thinking is disabled by default. But DeepSeek's/anthropicendpoint treats the absent parameter as "thinking enabled", causing every request withenabled=Falseto fail with HTTP 400:The fix
Added an
elifbranch inside the existingreasoning_configblock atagent/anthropic_adapter.py:_is_third_party_anthropic_endpointreturnsTruefor any non-anthropic.combase URL. Kimi/codingis already excluded by the outernot _is_kimi_codingguard.Test plan
assert None == {'type': 'disabled'}) — regression guard confirmedtest_reasoning_disabled_third_party_sends_explicit_disabledandtest_reasoning_disabled_third_party_any_providerboth fail withAssertionError: assert None == {'type': 'disabled'}; restored → 0 failurestests/agent/test_anthropic_adapter.py— 131 passed, 10 pre-existing baseline failures (unchanged)tests/agent/test_kimi_coding_anthropic_thinking.py— 8 passed (unchanged)test_reasoning_disabledstill 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/codinggets neither enabled nor disabledtest_reasoning_none_third_party_omits_thinking—reasoning_config=Noneon third-party endpoint still omitsthinkingRelated
🤖 Generated with Claude Code