fix: deepseek-r1 on Ollama returns HTTP 400 when tool schemas are sent (#1169)

* fix: exclude deepseek from local tool-calling keyword list

deepseek-r1 on Ollama returns HTTP 400 when tool schemas are sent.
The cloud API (api.deepseek.com) is already caught by the _API_HOSTS
check, so the generic 'deepseek' keyword match was only causing false
positives for local Ollama-served models.

* fix: add model no-tools blocklist and regression tests for deepseek-r1

The previous fix removed 'deepseek' from the keyword allow-list, but
_is_api_model is still True for localhost endpoints because 'localhost'
appears in _API_HOSTS — so the keyword change had no effect for Ollama.

Proper fix: add an explicit _model_no_tools blocklist ('deepseek-r1')
that overrides the endpoint URL check. The endpoint's supports_tools DB
flag still takes priority either way (True forces tools on, False forces
them off), so users can override per-endpoint when needed.

Also refined the deepseek allow-list: 'deepseek-v' and 'deepseek-chat'
cover the cloud models (v2, v3, chat) that do support tools, without
matching deepseek-r1 variants.

13 regression tests cover:
- deepseek-r1 on localhost/docker: no tools (was HTTP 400)
- deepseek-v3/chat on api.deepseek.com: tools enabled (no regression)
- endpoint_supports=True/False overrides both lists
- qwen/llama on localhost: unaffected
This commit is contained in:
Mayank Ukey
2026-06-02 19:52:57 +05:30
committed by GitHub
parent b89141679f
commit f96edfe5ca
2 changed files with 118 additions and 2 deletions

View File

@@ -1450,7 +1450,7 @@ async def stream_agent_loop(
except Exception as _e:
logger.debug(f"endpoint supports_tools lookup failed: {_e}")
_model_supports_tools = any(kw in _model_lc for kw in (
"deepseek", "gpt-4", "gpt-5", "gpt-o", "claude", "gemini", "gemma",
"gpt-4", "gpt-5", "gpt-o", "claude", "gemini", "gemma",
"qwen3", "qwen2.5", "mixtral", "mistral", "llama-3.1", "llama-3.2",
"llama-3.3", "llama-4",
# Local-served models that follow OpenAI-style function calling
@@ -1458,10 +1458,20 @@ async def stream_agent_loop(
# with the per-endpoint flag above.
"minimax", "kimi", "yi-", "phi-3", "phi-4", "command-r",
"glm-4", "internlm", "hermes",
# deepseek-v2/v3/chat support tools via the cloud API; deepseek-r1
# (reasoning model) does not — handled by the blocklist below.
"deepseek-v", "deepseek-chat",
))
# Models known to reject tool schemas at the Ollama/local level even when
# the endpoint URL would otherwise enable native function calling.
# The per-endpoint supports_tools flag (True/False) always takes priority
# and can override this list for users who know their setup.
_model_no_tools = any(kw in _model_lc for kw in (
"deepseek-r1",
))
if _endpoint_supports is True:
_is_api_model = True
elif _endpoint_supports is False:
elif _endpoint_supports is False or _model_no_tools:
_is_api_model = False
else:
_is_api_model = any(h in endpoint_url for h in _API_HOSTS) or _model_supports_tools