[Bugfix][Parser] Seed-OSS turn-boundary tokens + boundary-fallback tests (#54264)
Signed-off-by: Mikhail Podvitskii <podvitskiymichael@gmail.com> Co-authored-by: Flora Feng <4florafeng@gmail.com>
This commit is contained in:
co-authored by
Flora Feng
parent
4ebf61ebda
commit
65f3fca568
@@ -261,6 +261,19 @@ class TestIsReasoningEndTurnBoundaries:
|
||||
parser = Qwen3Parser(make_mock_tokenizer(vocab))
|
||||
assert parser.is_reasoning_end([_TEXT_ID, _THINK_END_ID, _TEXT_ID])
|
||||
|
||||
def test_boundary_with_thinking_disabled_is_end(self, mock_tokenizer):
|
||||
"""With thinking disabled (initial state CONTENT) a walk that stops at a turn
|
||||
boundary must report reasoning as ended: the model never emits a marker."""
|
||||
parser = Qwen3Parser(
|
||||
mock_tokenizer, chat_template_kwargs={"enable_thinking": False}
|
||||
)
|
||||
assert parser.is_reasoning_end([_IM_START_ID, _TEXT_ID])
|
||||
|
||||
def test_boundary_with_thinking_enabled_not_end(self, parser):
|
||||
"""With thinking disabled (initial state REASONING) a walk that stops at a turn
|
||||
boundary must keep reasoning open"""
|
||||
assert not parser.is_reasoning_end([_IM_START_ID, _TEXT_ID])
|
||||
|
||||
|
||||
class TestDelegatingPromptDetection:
|
||||
def test_prompt_tool_example_does_not_skip_streaming_reasoning(
|
||||
|
||||
@@ -33,15 +33,24 @@ TOOL_CALL_START = "<seed:tool_call>"
|
||||
TOOL_CALL_END = "</seed:tool_call>"
|
||||
THINK_START = "<seed:think>"
|
||||
THINK_END = "</seed:think>"
|
||||
TURN_START = "<seed:bos>"
|
||||
TURN_END = "<seed:eos>"
|
||||
|
||||
_THINK_START_ID = 50
|
||||
_THINK_END_ID = 51
|
||||
_TOOL_CALL_ID = 60
|
||||
_TOOL_CALL_START_ID = 60
|
||||
_TOOL_CALL_END_ID = 61
|
||||
_SEED_BOS_ID = 70
|
||||
_SEED_EOS_ID = 71
|
||||
_TEXT_ID = 100
|
||||
|
||||
_SEED_OSS_VOCAB = {
|
||||
THINK_START: 50,
|
||||
THINK_START: _THINK_START_ID,
|
||||
THINK_END: _THINK_END_ID,
|
||||
TOOL_CALL_START: _TOOL_CALL_ID,
|
||||
TOOL_CALL_END: 61,
|
||||
TOOL_CALL_START: _TOOL_CALL_START_ID,
|
||||
TOOL_CALL_END: _TOOL_CALL_END_ID,
|
||||
TURN_START: _SEED_BOS_ID,
|
||||
TURN_END: _SEED_EOS_ID,
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +75,43 @@ def test_token_overrides_wired(parser):
|
||||
assert parser.parser_engine_config.name == "seed_oss"
|
||||
assert parser.reasoning_start_str == THINK_START
|
||||
assert parser.reasoning_end_str == THINK_END
|
||||
assert parser.parser_engine_config.turn_boundary_tokens == frozenset(
|
||||
(TURN_START, TURN_END)
|
||||
)
|
||||
|
||||
|
||||
class TestSeedTurnBoundaries:
|
||||
"""The Seed-OSS chat template wraps every message in ``<seed:bos>role`` /
|
||||
``<seed:eos>`` and replays prior-turn ``reasoning_content`` inside
|
||||
``<seed:think>...</seed:think>``, so a stale ``</seed:think>`` in history
|
||||
is the normal multi-turn case. The backward walk must stop at the Seed
|
||||
boundary tokens; the inherited ChatML tokens are absent from the Seed
|
||||
vocab and would disable the scoping entirely.
|
||||
"""
|
||||
|
||||
def test_replayed_reasoning_in_history_not_end(self, parser):
|
||||
assert not parser.is_reasoning_end(
|
||||
[
|
||||
_SEED_BOS_ID,
|
||||
_THINK_START_ID,
|
||||
_TEXT_ID,
|
||||
_THINK_END_ID,
|
||||
_TEXT_ID,
|
||||
_SEED_EOS_ID,
|
||||
_SEED_BOS_ID,
|
||||
]
|
||||
)
|
||||
|
||||
def test_think_end_in_current_turn_is_end(self, parser):
|
||||
assert parser.is_reasoning_end(
|
||||
[_SEED_BOS_ID, _TEXT_ID, _SEED_EOS_ID, _SEED_BOS_ID, _THINK_END_ID]
|
||||
)
|
||||
|
||||
def test_boundary_with_thinking_disabled_is_end(self, mock_tokenizer):
|
||||
parser = SeedOssParser(
|
||||
mock_tokenizer, chat_template_kwargs={"enable_thinking": False}
|
||||
)
|
||||
assert parser.is_reasoning_end([_SEED_BOS_ID, _TEXT_ID])
|
||||
|
||||
|
||||
def test_single_tool_call(tool_parser, mock_request):
|
||||
@@ -133,7 +179,7 @@ def test_streaming_think_end_and_tool_call_same_delta(parser):
|
||||
f"{THINK_END}{TOOL_CALL_START}",
|
||||
"<function=read>",
|
||||
],
|
||||
[(1,), (_THINK_END_ID, _TOOL_CALL_ID), (2,)],
|
||||
[(1,), (_THINK_END_ID, _TOOL_CALL_START_ID), (2,)],
|
||||
)
|
||||
assert reasoning == "Let me list the directory."
|
||||
assert THINK_END not in reasoning
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""seed_oss parser for tool calls and reasoning.
|
||||
|
||||
seed_oss shares the Qwen3 XML grammar exactly; only the four wrapper
|
||||
token strings differ::
|
||||
seed_oss shares the Qwen3 XML grammar exactly; only the wrapper token
|
||||
strings and the turn-boundary tokens differ::
|
||||
|
||||
<think> -> <seed:think>
|
||||
</think> -> </seed:think>
|
||||
<tool_call> -> <seed:tool_call>
|
||||
</tool_call> -> </seed:tool_call>
|
||||
<|im_start|> -> <seed:bos>
|
||||
<|im_end|> -> <seed:eos>
|
||||
|
||||
``<function=...>`` and ``<parameter=...>`` are byte-identical, so the
|
||||
entire transition table and ``_qwen3_arg_converter`` are inherited from
|
||||
@@ -26,3 +28,4 @@ class SeedOssParser(Qwen3Parser):
|
||||
THINK_END = "</seed:think>"
|
||||
TOOL_START = "<seed:tool_call>"
|
||||
TOOL_END = "</seed:tool_call>"
|
||||
TURN_BOUNDARIES = frozenset(("<seed:bos>", "<seed:eos>"))
|
||||
|
||||
Reference in New Issue
Block a user