forked from Karylab-cklius/vllm
[Bug][Structured Outputs] Fix bug that leads to unconstrained generations with structural tags (#42452)
Signed-off-by: rishitdholakia13 <rishit+github@cohere.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
df84fb07a6
commit
ded871201a
@@ -10,6 +10,7 @@ import pytest
|
||||
from vllm.config import ModelConfig, SchedulerConfig, VllmConfig
|
||||
from vllm.v1.request import Request
|
||||
from vllm.v1.structured_output import StructuredOutputManager
|
||||
from vllm.v1.structured_output.backend_types import StructuredOutputOptions
|
||||
|
||||
|
||||
class MockReasoner:
|
||||
@@ -214,6 +215,32 @@ class TestReasoningStructuredOutput:
|
||||
)
|
||||
assert result is False
|
||||
|
||||
def test_should_advance_reasoning_just_ended_with_spec_decode_structural_tag(
|
||||
self,
|
||||
manager_with_reasoner,
|
||||
mock_request_with_structured_output,
|
||||
):
|
||||
"""When reasoning ends this step, advance immediately for structural
|
||||
tags with speculative decoding."""
|
||||
structured_req = mock_request_with_structured_output.structured_output_request
|
||||
structured_req.reasoning_ended = False
|
||||
structured_req.structured_output_key = (
|
||||
StructuredOutputOptions.STRUCTURAL_TAG,
|
||||
"{}",
|
||||
)
|
||||
reasoner = MockReasoner(tokenizer=Mock())
|
||||
reasoner.is_reasoning_end_streaming.return_value = True
|
||||
structured_req.reasoner = reasoner
|
||||
|
||||
manager_with_reasoner.vllm_config.speculative_config = Mock()
|
||||
|
||||
result = manager_with_reasoner.should_advance(
|
||||
mock_request_with_structured_output
|
||||
)
|
||||
|
||||
assert structured_req.reasoning_ended is True
|
||||
assert result is True
|
||||
|
||||
def test_should_advance_reasoning_already_ended(
|
||||
self,
|
||||
manager_with_reasoner,
|
||||
|
||||
@@ -15,6 +15,7 @@ from vllm.v1.structured_output.backend_guidance import GuidanceBackend
|
||||
from vllm.v1.structured_output.backend_types import (
|
||||
StructuredOutputBackend,
|
||||
StructuredOutputGrammar,
|
||||
StructuredOutputOptions,
|
||||
)
|
||||
from vllm.v1.structured_output.backend_xgrammar import XgrammarBackend
|
||||
|
||||
@@ -350,10 +351,22 @@ class StructuredOutputManager:
|
||||
if reasoner.is_reasoning_end_streaming(
|
||||
all_token_ids, itertools.islice(all_token_ids, start, None)
|
||||
):
|
||||
# Reasoning just ended, so we shouldn't advance til
|
||||
# next pass
|
||||
structured_req.reasoning_ended = True
|
||||
|
||||
# Reasoning just ended this step. Defer FSM advance until the next
|
||||
# pass (see reasoning_ended check above) for JSON/regex/choice/grammar:
|
||||
# advancing on the closing boundary token can accept tokens that still
|
||||
# belong to the reasoning stream. Structural tags are the only safe
|
||||
# same-step exception: they model phased output (e.g. thinking tag ->
|
||||
# answer tag), and speculative decoding must run grammar.validate_tokens
|
||||
# on draft tokens produced immediately after that transition.
|
||||
if (
|
||||
self.vllm_config.speculative_config is not None
|
||||
and structured_req.structured_output_key[0]
|
||||
== StructuredOutputOptions.STRUCTURAL_TAG
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def clear_backend(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user