# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Tests for the engine-based seed_oss parser. seed_oss is Qwen3 with four overridden wrapper tokens, so the shared grammar (arg types, multiline values, parallel calls, streaming mechanics, …) is already covered by ``test_qwen3.py``/``test_qwen3_reasoning.py``. These tests cover only what is seed_oss-specific: that the ``seed:`` token overrides are wired through, the reasoning→tool boundary holds with them, the malformed header from #46314 no longer drops sibling calls, and the registered adapters resolve. Seed-specific budget-reflect tags inside reasoning are also covered here because the old dedicated parser tests exercised them. """ import json import pytest from tests.parser.engine.conftest import make_mock_tokenizer from tests.parser.engine.streaming_helpers import ( collect_function_name, collect_tool_arguments, simulate_reasoning_streaming, simulate_tool_streaming, ) from vllm.parser.engine.registered_adapters import ( SeedOssParserReasoningAdapter, SeedOssParserToolAdapter, ) from vllm.parser.seed_oss import SeedOssParser TOOL_CALL_START = "" TOOL_CALL_END = "" THINK_START = "" THINK_END = "" _THINK_END_ID = 51 _TOOL_CALL_ID = 60 _SEED_OSS_VOCAB = { THINK_START: 50, THINK_END: _THINK_END_ID, TOOL_CALL_START: _TOOL_CALL_ID, TOOL_CALL_END: 61, } @pytest.fixture def mock_tokenizer(): return make_mock_tokenizer(_SEED_OSS_VOCAB) @pytest.fixture def tool_parser(mock_tokenizer): return SeedOssParser( mock_tokenizer, chat_template_kwargs={"enable_thinking": False} ) @pytest.fixture def parser(mock_tokenizer): return SeedOssParser(mock_tokenizer) 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 def test_single_tool_call(tool_parser, mock_request): text = ( f"{TOOL_CALL_START}\n\n" "Tokyo\n" f"\n{TOOL_CALL_END}" ) result = tool_parser.extract_tool_calls(text, mock_request) assert result.tools_called is True assert result.tool_calls[0].function.name == "get_weather" assert json.loads(result.tool_calls[0].function.arguments) == {"city": "Tokyo"} def test_malformed_function_end_does_not_drop_siblings(tool_parser, mock_request): """Regression for #46314: a malformed ```` with no closing ``>`` on the header must not discard the other, well-formed calls.""" text = ( f"{TOOL_CALL_START}\n\n{TOOL_CALL_END}" f"{TOOL_CALL_START}\n\n" "Tokyo\n" f"\n{TOOL_CALL_END}" ) result = tool_parser.extract_tool_calls(text, mock_request) weather = next(tc for tc in result.tool_calls if tc.function.name == "get_weather") assert json.loads(weather.function.arguments) == {"city": "Tokyo"} def test_basic_streaming(tool_parser, mock_request): chunks = [ f"{TOOL_CALL_START}\n", "\n", "Tokyo", "\n", "\n", f"{TOOL_CALL_END}", ] results = simulate_tool_streaming(tool_parser, mock_request, chunks) assert collect_function_name(results) == "get_weather" assert json.loads(collect_tool_arguments(results)) == {"city": "Tokyo"} def test_reasoning_then_tool_call(parser): text = ( f"{THINK_START}I need to read the file.{THINK_END}" f"{TOOL_CALL_START}\n\n" "/tmp/x\n" f"\n{TOOL_CALL_END}" ) reasoning, _ = parser.extract_reasoning(text, None) assert reasoning == "I need to read the file." assert TOOL_CALL_START not in reasoning def test_streaming_think_end_and_tool_call_same_delta(parser): """```` and ```` arriving in one delta must not leak the terminal tokens into the reasoning text.""" reasoning, content = simulate_reasoning_streaming( parser, [ "Let me list the directory.", f"{THINK_END}{TOOL_CALL_START}", "", ], [(1,), (_THINK_END_ID, _TOOL_CALL_ID), (2,)], ) assert reasoning == "Let me list the directory." assert THINK_END not in reasoning assert TOOL_CALL_START not in reasoning assert content is not None def test_end_to_end_through_registered_adapters(mock_tokenizer, mock_request): reasoning_parser = SeedOssParserReasoningAdapter(mock_tokenizer) tool_parser = SeedOssParserToolAdapter(mock_tokenizer) text = ( f"{THINK_START}Plan the call.{THINK_END}" f"{TOOL_CALL_START}\n\n" "Tokyo\n" f"\n{TOOL_CALL_END}" ) reasoning, remaining = reasoning_parser.extract_reasoning(text, mock_request) assert reasoning == "Plan the call." tool_result = tool_parser.extract_tool_calls(remaining, mock_request) assert tool_result.tool_calls[0].function.name == "get_weather" assert json.loads(tool_result.tool_calls[0].function.arguments) == {"city": "Tokyo"} def test_budget_reflect_tags_do_not_break_adapter_pipeline( mock_tokenizer, mock_request, ): reasoning_parser = SeedOssParserReasoningAdapter(mock_tokenizer) tool_parser = SeedOssParserToolAdapter(mock_tokenizer) text = ( f"{THINK_START}" "The user's current thinking budget is 512.\n" "I need the weather.\n" "I have used 131 tokens." "\n" f"{THINK_END}" f"{TOOL_CALL_START}\n\n" "Barcelona\n" f"\n{TOOL_CALL_END}" ) reasoning, remaining = reasoning_parser.extract_reasoning(text, mock_request) assert reasoning is not None assert "current thinking budget is 512" in reasoning assert "" in reasoning assert "" in reasoning tool_result = tool_parser.extract_tool_calls(remaining, mock_request) assert tool_result.tool_calls[0].function.name == "get_weather" assert json.loads(tool_result.tool_calls[0].function.arguments) == { "city": "Barcelona" }