From 7f98e675d55027166595b198f99991a96085fb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yi=C4=9Fit=20ERDO=C4=9EAN?= Date: Tue, 15 Sep 2026 19:33:16 +0300 Subject: [PATCH] [#19225][fix] Keep markdown fences in DeepSeek V3 streamed content MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeepSeekV3Parser.parse_streaming_increment removes every "```" from a delta that carries no tool-call markup, so a streamed answer containing a fenced code block loses its fences while detect_and_parse returns the same text intact. The fence is only part of the tool-call syntax inside a <|tool▁call▁begin|> ... <|tool▁call▁end|> block, which this branch never sees. Strip only the closing tokens, as DeepSeekV31Parser does. Signed-off-by: Yiğit ERDOĞAN --- .../serve/tool_parser/deepseekv3_parser.py | 2 +- tests/unittest/llmapi/apps/test_tool_parsers.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/tensorrt_llm/serve/tool_parser/deepseekv3_parser.py b/tensorrt_llm/serve/tool_parser/deepseekv3_parser.py index 8cbe2a8c2aef..956dec7ef33a 100644 --- a/tensorrt_llm/serve/tool_parser/deepseekv3_parser.py +++ b/tensorrt_llm/serve/tool_parser/deepseekv3_parser.py @@ -109,7 +109,7 @@ def parse_streaming_increment(self, new_text: str, tools: List[Tool]) -> Streami return StreamingParseResult() normal_text = current_text self._buffer = "" - for e_token in [self.eot_token, "```", "<|tool▁call▁end|>"]: + for e_token in [self.eot_token, "<|tool▁call▁end|>"]: normal_text = normal_text.replace(e_token, "") return StreamingParseResult(normal_text=normal_text) diff --git a/tests/unittest/llmapi/apps/test_tool_parsers.py b/tests/unittest/llmapi/apps/test_tool_parsers.py index ccc82e9adc7e..adababa706e2 100644 --- a/tests/unittest/llmapi/apps/test_tool_parsers.py +++ b/tests/unittest/llmapi/apps/test_tool_parsers.py @@ -1899,6 +1899,22 @@ def test_deepseek_streaming_preserves_withheld_text( sample_tools).normal_text == expected +@pytest.mark.parametrize( + "parser_cls", + [DeepSeekV3Parser, DeepSeekV31Parser, DeepSeekV32Parser, DeepSeekV4Parser]) +def test_deepseek_streaming_keeps_markdown_fences( + sample_tools: list[ChatCompletionToolsParam], + parser_cls: type[BaseToolParser]) -> None: + """Content without tool-call markup is streamed verbatim.""" + text = "Here is the code:\n```python\nprint(1)\n```\nDone." + + streamed = parser_cls().parse_streaming_increment(text, + sample_tools).normal_text + + assert streamed == text, f"Expected {text!r}, got {streamed!r}" + assert parser_cls().detect_and_parse(text, sample_tools).normal_text == text + + @pytest.mark.parametrize( "parser_cls, tool_call_text", [