From c000bf95671314849bf5852b5777bcd9f0919c8c Mon Sep 17 00:00:00 2001 From: Milan Date: Thu, 11 Jun 2026 23:41:55 +0300 Subject: [PATCH] test(deepseek): move V4 thinking-mode tests to tests/test_litellm for CI coverage Codecov patch coverage runs the tests/test_litellm suite; the new tests lived in tests/litellm and were not executed there. Also adds an async_transform_request test. Co-authored-by: Cursor --- .../chat/test_deepseek_chat_transformation.py | 126 ------------------ .../test_deepseek_chat_transformation.py | 115 ++++++++++++++++ 2 files changed, 115 insertions(+), 126 deletions(-) create mode 100644 tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py diff --git a/tests/litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py b/tests/litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py index d74fb533e1a..a2f45e7188b 100644 --- a/tests/litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py +++ b/tests/litellm/llms/deepseek/chat/test_deepseek_chat_transformation.py @@ -166,129 +166,3 @@ class TestDeepSeekThinkingParams: ) assert "thinking" not in result - - def test_map_thinking_disabled_passed_through(self): - """thinking={"type": "disabled"} is forwarded (opt-out of V4 default thinking).""" - result = self.config.map_openai_params( - non_default_params={"thinking": {"type": "disabled"}}, - optional_params={}, - model="deepseek-v4-flash", - drop_params=False, - ) - - assert result["thinking"] == {"type": "disabled"} - - -class TestDeepSeekV4DefaultThinkingMode: - """DeepSeek V4 default-on thinking mode / reasoning_content pass-back.""" - - def setup_method(self): - self.config = DeepSeekChatConfig() - - # --- registry --- - - @pytest.mark.parametrize( - "model", - ["deepseek-v4-flash", "deepseek-v4-pro"], - ) - def test_v4_models_registered_with_reasoning(self, model): - from litellm.utils import supports_reasoning - - assert supports_reasoning(model=model, custom_llm_provider="deepseek") - assert supports_reasoning(model=f"deepseek/{model}") - - # --- _thinking_mode_active guard --- - - @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) - def test_thinking_active_by_default_for_v4(self, model): - assert self.config._thinking_mode_active(model=model, optional_params={}) - - @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) - def test_thinking_inactive_when_explicitly_disabled(self, model): - assert not self.config._thinking_mode_active( - model=model, optional_params={"thinking": {"type": "disabled"}} - ) - - @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) - def test_thinking_active_when_explicitly_enabled(self, model): - assert self.config._thinking_mode_active( - model=model, optional_params={"thinking": {"type": "enabled"}} - ) - - def test_reasoning_capable_models_active_by_default(self): - assert self.config._thinking_mode_active( - model="deepseek-v3.2", optional_params={} - ) - assert self.config._thinking_mode_active( - model="deepseek-v3.2", optional_params={"thinking": {"type": "enabled"}} - ) - - def test_non_reasoning_model_unaffected(self): - assert not self.config._thinking_mode_active( - model="deepseek-chat", optional_params={} - ) - - # --- end-to-end transform_request --- - - def _tool_call_history(self): - return [ - {"role": "user", "content": "What's the weather in Tokyo?"}, - { - "role": "assistant", - "content": None, - "tool_calls": [ - { - "id": "call_client_generated_id", - "type": "function", - "function": { - "name": "get_weather", - "arguments": '{"city": "Tokyo"}', - }, - } - ], - # reasoning_content stripped, tool_call id rewritten: - # standard behavior of frontends/agent frameworks, and the - # exact request shape DeepSeek rejects with - # "The `reasoning_content` in the thinking mode must be passed back to the API." - }, - { - "role": "tool", - "tool_call_id": "call_client_generated_id", - "content": '{"weather": "Sunny", "temp_c": 28}', - }, - ] - - def test_transform_request_injects_reasoning_content_for_v4_by_default(self): - body = self.config.transform_request( - model="deepseek-v4-flash", - messages=self._tool_call_history(), - optional_params={}, - litellm_params={}, - headers={}, - ) - assistant_msg = body["messages"][1] - assert assistant_msg["reasoning_content"] == " " - - def test_transform_request_no_injection_when_thinking_disabled(self): - body = self.config.transform_request( - model="deepseek-v4-flash", - messages=self._tool_call_history(), - optional_params={"thinking": {"type": "disabled"}}, - litellm_params={}, - headers={}, - ) - assistant_msg = body["messages"][1] - assert "reasoning_content" not in assistant_msg - - def test_transform_request_preserves_existing_reasoning_content(self): - messages = self._tool_call_history() - messages[1]["reasoning_content"] = "I should check the weather tool." - body = self.config.transform_request( - model="deepseek-v4-pro", - messages=messages, - optional_params={}, - litellm_params={}, - headers={}, - ) - assistant_msg = body["messages"][1] - assert assistant_msg["reasoning_content"] == "I should check the weather tool." diff --git a/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py b/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py new file mode 100644 index 00000000000..e64c6dbc59b --- /dev/null +++ b/tests/test_litellm/llms/deepseek/test_deepseek_chat_transformation.py @@ -0,0 +1,115 @@ +"""Tests for DeepSeek V4 default-on thinking mode / reasoning_content pass-back.""" + +import pytest + +from litellm.llms.deepseek.chat.transformation import DeepSeekChatConfig + + +class TestDeepSeekV4DefaultThinkingMode: + def setup_method(self): + self.config = DeepSeekChatConfig() + + @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) + def test_v4_models_registered_with_reasoning(self, model): + from litellm.utils import supports_reasoning + + assert supports_reasoning(model=model, custom_llm_provider="deepseek") + assert supports_reasoning(model=f"deepseek/{model}") + + def test_map_thinking_disabled_passed_through(self): + result = self.config.map_openai_params( + non_default_params={"thinking": {"type": "disabled"}}, + optional_params={}, + model="deepseek-v4-flash", + drop_params=False, + ) + + assert result["thinking"] == {"type": "disabled"} + + @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) + def test_thinking_active_by_default_for_v4(self, model): + assert self.config._thinking_mode_active(model=model, optional_params={}) + + @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) + def test_thinking_inactive_when_explicitly_disabled(self, model): + assert not self.config._thinking_mode_active( + model=model, optional_params={"thinking": {"type": "disabled"}} + ) + + @pytest.mark.parametrize("model", ["deepseek-v4-flash", "deepseek-v4-pro"]) + def test_thinking_active_when_explicitly_enabled(self, model): + assert self.config._thinking_mode_active( + model=model, optional_params={"thinking": {"type": "enabled"}} + ) + + def test_non_reasoning_model_unaffected(self): + assert not self.config._thinking_mode_active( + model="deepseek-chat", optional_params={} + ) + + def _tool_call_history(self): + return [ + {"role": "user", "content": "What's the weather in Tokyo?"}, + { + "role": "assistant", + "content": None, + "tool_calls": [ + { + "id": "call_client_generated_id", + "type": "function", + "function": { + "name": "get_weather", + "arguments": '{"city": "Tokyo"}', + }, + } + ], + }, + { + "role": "tool", + "tool_call_id": "call_client_generated_id", + "content": '{"weather": "Sunny", "temp_c": 28}', + }, + ] + + def test_transform_request_injects_reasoning_content_for_v4_by_default(self): + body = self.config.transform_request( + model="deepseek-v4-flash", + messages=self._tool_call_history(), + optional_params={}, + litellm_params={}, + headers={}, + ) + assert body["messages"][1]["reasoning_content"] == " " + + def test_transform_request_no_injection_when_thinking_disabled(self): + body = self.config.transform_request( + model="deepseek-v4-flash", + messages=self._tool_call_history(), + optional_params={"thinking": {"type": "disabled"}}, + litellm_params={}, + headers={}, + ) + assert "reasoning_content" not in body["messages"][1] + + def test_transform_request_preserves_existing_reasoning_content(self): + messages = self._tool_call_history() + messages[1]["reasoning_content"] = "I should check the weather tool." + body = self.config.transform_request( + model="deepseek-v4-pro", + messages=messages, + optional_params={}, + litellm_params={}, + headers={}, + ) + assert body["messages"][1]["reasoning_content"] == "I should check the weather tool." + + @pytest.mark.asyncio + async def test_async_transform_request_injects_reasoning_content(self): + body = await self.config.async_transform_request( + model="deepseek-v4-flash", + messages=self._tool_call_history(), + optional_params={}, + litellm_params={}, + headers={}, + ) + assert body["messages"][1]["reasoning_content"] == " "