support Hy3 preview (#23533)

Co-authored-by: pengmeng <pengmeng@tencent.com>
Co-authored-by: Qiaolin-Yu <liin1211@outlook.com>
Co-authored-by: chengvjiang <chengvjiang@tencent.com>
Co-authored-by: russellfeng <russellfeng@tencent.com>
This commit is contained in:
Xinyuan Tong
2026-04-24 12:03:24 -07:00
committed by GitHub
co-authored by pengmeng Qiaolin-Yu chengvjiang russellfeng
parent 6344b546c8
commit 6d03861476
25 changed files with 4095 additions and 3 deletions
@@ -7,6 +7,7 @@ from sglang.srt.parser.reasoning_parser import (
DeepSeekR1Detector,
Gemma4Detector,
Glm45Detector,
HunyuanDetector,
KimiDetector,
KimiK2Detector,
Nemotron3Detector,
@@ -519,6 +520,99 @@ class TestGlm45Detector(CustomTestCase):
self.assertEqual(result.normal_text, "<tool_call>tool call")
class TestHunyuanDetector(CustomTestCase):
"""Test cases for Hunyuan detector with tool interruption support."""
def setUp(self):
self.detector = HunyuanDetector()
def test_init(self):
"""Test HunyuanDetector initialization."""
self.assertEqual(self.detector.think_start_token, "<think>")
self.assertEqual(self.detector.think_end_token, "</think>")
self.assertEqual(self.detector.tool_start_token, "<tool_calls>")
self.assertFalse(self.detector._in_reasoning)
self.assertTrue(self.detector.stream_reasoning)
def test_detect_and_parse_normal_reasoning(self):
"""Test parsing normal reasoning block without tool interruption."""
text = "<think>Let me think about this</think>The answer is 42."
result = self.detector.detect_and_parse(text)
self.assertEqual(result.reasoning_text, "Let me think about this")
self.assertEqual(result.normal_text, "The answer is 42.")
def test_detect_and_parse_without_thinking(self):
"""Test parsing without thinking tokens (no_think mode)."""
text = "Direct answer without thinking."
result = self.detector.detect_and_parse(text)
self.assertEqual(result.normal_text, text)
self.assertEqual(result.reasoning_text, "")
def test_detect_and_parse_tool_interrupt(self):
"""Test parsing with tool call interruption during reasoning."""
text = "<think>I need to check<tool_calls><tool_call>get_weather<tool_sep></tool_call></tool_calls>"
result = self.detector.detect_and_parse(text)
self.assertEqual(result.reasoning_text, "I need to check")
self.assertIn("<tool_calls>", result.normal_text)
def test_streaming_normal_reasoning(self):
"""Test streaming parse of normal reasoning block."""
self.detector.parse_streaming_increment("<think>")
result1 = self.detector.parse_streaming_increment("reasoning content")
self.assertEqual(result1.reasoning_text, "reasoning content")
result2 = self.detector.parse_streaming_increment("</think>answer")
self.assertEqual(result2.normal_text, "answer")
self.assertFalse(self.detector._in_reasoning)
def test_streaming_tool_interrupt(self):
"""Test streaming parse interrupted by tool call section."""
self.detector.parse_streaming_increment("<think>")
result1 = self.detector.parse_streaming_increment("thinking")
self.assertEqual(result1.reasoning_text, "thinking")
result2 = self.detector.parse_streaming_increment("<tool_calls>")
self.assertEqual(result2.reasoning_text, "")
self.assertEqual(result2.normal_text, "<tool_calls>")
self.assertFalse(self.detector._in_reasoning)
def test_streaming_after_interrupt_is_normal(self):
"""After tool interruption, subsequent chunks should be normal text."""
self.detector.parse_streaming_increment("<think>")
self.detector.parse_streaming_increment("reasoning<tool_calls>")
result = self.detector.parse_streaming_increment("<tool_call>data")
self.assertEqual(result.reasoning_text, "")
self.assertEqual(result.normal_text, "<tool_call>data")
def test_reasoning_parser_integration(self):
"""Test Hunyuan through ReasoningParser API."""
parser = ReasoningParser("hunyuan")
self.assertIsInstance(parser.detector, HunyuanDetector)
# Non-streaming
reasoning, normal = parser.parse_non_stream(
"<think>thinking<tool_calls><tool_call>func<tool_sep></tool_call></tool_calls>"
)
self.assertEqual(reasoning, "thinking")
self.assertIn("<tool_calls>", normal)
def test_reasoning_parser_streaming(self):
"""Test Hunyuan streaming through ReasoningParser API."""
parser = ReasoningParser("hunyuan")
chunks = ["<think>", "reasoning", "<tool_calls>", "<tool_call>func"]
all_reasoning = ""
all_normal = ""
for chunk in chunks:
reasoning, normal = parser.parse_stream_chunk(chunk)
if reasoning:
all_reasoning += reasoning
if normal:
all_normal += normal
self.assertEqual(all_reasoning, "reasoning")
self.assertIn("<tool_calls>", all_normal)
class TestNemotron3Detector(CustomTestCase):
def setUp(self):
self.detector = Nemotron3Detector()
@@ -740,6 +834,9 @@ class TestReasoningParser(CustomTestCase):
parser = ReasoningParser("glm45")
self.assertIsInstance(parser.detector, Glm45Detector)
parser = ReasoningParser("hunyuan")
self.assertIsInstance(parser.detector, HunyuanDetector)
parser = ReasoningParser("gemma4")
self.assertIsInstance(parser.detector, Gemma4Detector)