Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions agent/transports/chat_completions.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ def build_kwargs(
is_github_models: bool
is_nvidia_nim: bool
is_kimi: bool
is_deepseek_direct: bool
is_lmstudio: bool
is_custom_provider: bool
ollama_num_ctx: int | None
Expand Down Expand Up @@ -353,6 +354,19 @@ def build_kwargs(
"type": "enabled" if _kimi_thinking_enabled else "disabled",
}

# DeepSeek direct API: extra_body.thinking
is_deepseek_direct = params.get("is_deepseek_direct", False)
if is_deepseek_direct:
_ds_thinking_enabled = True
if reasoning_config and isinstance(reasoning_config, dict):
if reasoning_config.get("enabled") is False:
_ds_thinking_enabled = False
elif (reasoning_config.get("effort") or "").strip().lower() == "none":
_ds_thinking_enabled = False
extra_body["thinking"] = {
"type": "enabled" if _ds_thinking_enabled else "disabled",
}

# Reasoning. LM Studio is handled above via top-level reasoning_effort,
# so skip emitting extra_body.reasoning for it.
if params.get("supports_reasoning", False) and not params.get("is_lmstudio", False):
Expand Down
5 changes: 5 additions & 0 deletions run_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -8410,6 +8410,10 @@ def _build_api_kwargs(self, api_messages: list) -> dict:
or base_url_host_matches(self.base_url, "moonshot.ai")
or base_url_host_matches(self.base_url, "moonshot.cn")
)
_is_deepseek_direct = (
(self.provider or "").strip().lower() == "deepseek"
or base_url_host_matches(self.base_url, "api.deepseek.com")
)
_is_tokenhub = base_url_host_matches(self._base_url_lower, "tokenhub.tencentmaas.com")
_is_lmstudio = (self.provider or "").strip().lower() == "lmstudio"

Expand Down Expand Up @@ -8484,6 +8488,7 @@ def _build_api_kwargs(self, api_messages: list) -> dict:
is_github_models=_is_gh,
is_nvidia_nim=_is_nvidia,
is_kimi=_is_kimi,
is_deepseek_direct=_is_deepseek_direct,
is_tokenhub=_is_tokenhub,
is_lmstudio=_is_lmstudio,
is_custom_provider=self.provider == "custom",
Expand Down
43 changes: 43 additions & 0 deletions tests/agent/transports/test_chat_completions.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,49 @@ def test_non_moonshot_tools_are_not_mutated(self, transport):
assert "type" not in kw["tools"][0]["function"]["parameters"]["properties"]["q"]


class TestChatCompletionsDeepSeekDirect:
"""DeepSeek direct API (api.deepseek.com) thinking control via extra_body."""

def test_deepseek_thinking_enabled_by_default(self, transport):
kw = transport.build_kwargs(
model="deepseek-chat", messages=[{"role": "user", "content": "Hi"}],
is_deepseek_direct=True,
)
assert kw["extra_body"]["thinking"] == {"type": "enabled"}

def test_deepseek_thinking_disabled_when_enabled_false(self, transport):
kw = transport.build_kwargs(
model="deepseek-chat", messages=[{"role": "user", "content": "Hi"}],
is_deepseek_direct=True,
reasoning_config={"enabled": False},
)
assert kw["extra_body"]["thinking"] == {"type": "disabled"}

def test_deepseek_thinking_disabled_when_effort_none(self, transport):
kw = transport.build_kwargs(
model="deepseek-chat", messages=[{"role": "user", "content": "Hi"}],
is_deepseek_direct=True,
reasoning_config={"effort": "none"},
)
assert kw["extra_body"]["thinking"] == {"type": "disabled"}

def test_openrouter_deepseek_no_thinking_param(self, transport):
kw = transport.build_kwargs(
model="deepseek/deepseek-chat",
messages=[{"role": "user", "content": "Hi"}],
is_openrouter=True,
)
assert "thinking" not in kw.get("extra_body", {})

def test_kimi_thinking_unchanged_with_deepseek_present(self, transport):
kw = transport.build_kwargs(
model="kimi-k2", messages=[{"role": "user", "content": "Hi"}],
is_kimi=True,
max_tokens_param_fn=lambda n: {"max_tokens": n},
)
assert kw["extra_body"]["thinking"] == {"type": "enabled"}


class TestChatCompletionsLmStudioReasoning:
"""LM Studio publishes per-model reasoning ``allowed_options``. When the
user requests an effort the model can't honor (e.g. ``high`` on a
Expand Down
Loading