diff --git a/tests/tokenizers_/test_deepseek_v41.py b/tests/tokenizers_/test_deepseek_v41.py index 35302c90361e..76fd1dcafc2f 100644 --- a/tests/tokenizers_/test_deepseek_v41.py +++ b/tests/tokenizers_/test_deepseek_v41.py @@ -89,6 +89,24 @@ def test_drop_thinking_keeps_current_turn_across_tool_results(): assert next_turn.endswith("q3<|Assistant|>") +@pytest.mark.parametrize("thinking", [False, True]) +def test_continue_final_message_keeps_the_last_assistant_turn_open(thinking): + messages = [ + {"role": "user", "content": "q"}, + {"role": "assistant", "content": "Sure, the answer is"}, + ] + closed = render(messages, thinking=thinking) + assert closed.endswith("Sure, the answer is<|end▁of▁sentence|>") + assert render( + messages, thinking=thinking, continue_final_message=True + ) == closed.removesuffix("<|end▁of▁sentence|>") + # Only the last message, and only an assistant one, is continued. + user_last = [*messages, {"role": "user", "content": "q2"}] + assert render(user_last, thinking=thinking, continue_final_message=True) == ( + render(user_last, thinking=thinking) + ) + + @pytest.mark.parametrize("role", ["user", "tool"]) @pytest.mark.parametrize("as_part", [False, True]) def test_rejects_image_placeholder_in_text(role, as_part): diff --git a/vllm/tokenizers/deepseek_v41.py b/vllm/tokenizers/deepseek_v41.py index 966dc7f9b9cf..192e1e053bab 100644 --- a/vllm/tokenizers/deepseek_v41.py +++ b/vllm/tokenizers/deepseek_v41.py @@ -85,6 +85,13 @@ def apply_chat_template( system = {"role": "system", "content": ""} conversation.insert(0, system) system["tools"] = tools + # Reference encoding.py: a continued assistant message has no EOS. + if ( + kwargs.get("continue_final_message") + and conversation + and conversation[-1]["role"] == "assistant" + ): + conversation[-1]["wo_eos"] = True thinking = bool(kwargs.get("thinking") or kwargs.get("enable_thinking")) if "thinking" not in kwargs and "enable_thinking" not in kwargs: