|
3 | 3 |
|
4 | 4 | """Unit tests for reasoning-aware structured output functionality (PR #25515).""" |
5 | 5 |
|
6 | | -from unittest.mock import Mock, patch |
| 6 | +from unittest.mock import Mock |
7 | 7 |
|
8 | 8 | import pytest |
9 | 9 |
|
@@ -209,37 +209,11 @@ def test_should_advance_reasoning_just_ended( |
209 | 209 | mock_request_with_structured_output |
210 | 210 | ) |
211 | 211 |
|
212 | | - # Should set reasoning_ended to True but return False for this step |
| 212 | + # The scheduler trims the reasoning prefix before advancing the grammar. |
213 | 213 | assert ( |
214 | 214 | mock_request_with_structured_output.structured_output_request.reasoning_ended |
215 | 215 | is True |
216 | 216 | ) |
217 | | - assert result is False |
218 | | - |
219 | | - def test_should_advance_reasoning_just_ended_with_spec_decode_structural_tag( |
220 | | - self, |
221 | | - manager_with_reasoner, |
222 | | - mock_request_with_structured_output, |
223 | | - ): |
224 | | - """When reasoning ends this step, advance immediately for structural |
225 | | - tags with speculative decoding.""" |
226 | | - structured_req = mock_request_with_structured_output.structured_output_request |
227 | | - structured_req.reasoning_ended = False |
228 | | - structured_req.structured_output_key = ( |
229 | | - StructuredOutputOptions.STRUCTURAL_TAG, |
230 | | - "{}", |
231 | | - ) |
232 | | - reasoner = MockReasoner(tokenizer=Mock()) |
233 | | - reasoner.is_reasoning_end_streaming.return_value = True |
234 | | - structured_req.reasoner = reasoner |
235 | | - |
236 | | - manager_with_reasoner.vllm_config.speculative_config = Mock() |
237 | | - |
238 | | - result = manager_with_reasoner.should_advance( |
239 | | - mock_request_with_structured_output |
240 | | - ) |
241 | | - |
242 | | - assert structured_req.reasoning_ended is True |
243 | 217 | assert result is True |
244 | 218 |
|
245 | 219 | def test_should_advance_reasoning_already_ended( |
@@ -305,7 +279,7 @@ def test_should_advance_uses_new_token_ids_when_provided( |
305 | 279 | assert list(called_delta) == new_token_ids |
306 | 280 |
|
307 | 281 | assert structured_req.reasoning_ended is True |
308 | | - assert result is False |
| 282 | + assert result is True |
309 | 283 |
|
310 | 284 | def test_should_advance_without_new_token_ids_falls_back( |
311 | 285 | self, |
@@ -334,16 +308,12 @@ def test_should_advance_without_new_token_ids_falls_back( |
334 | 308 | assert list(called_delta) == [4, 5] |
335 | 309 | assert result is False |
336 | 310 |
|
337 | | - def test_should_advance_drains_post_marker_into_grammar( |
| 311 | + def test_should_advance_trims_reasoning_prefix_for_json( |
338 | 312 | self, |
339 | 313 | manager_with_reasoner, |
340 | 314 | mock_request_with_structured_output, |
341 | 315 | ): |
342 | | - """On the step that ends reasoning, post-marker content tokens are |
343 | | - fed to the grammar so the next step's bitmask reflects the post- |
344 | | - marker FSM state. Without this, the model can emit a duplicate |
345 | | - opening token (e.g. "{{" for json_object). |
346 | | - """ |
| 316 | + """JSON uses the common trim-then-advance path at the boundary.""" |
347 | 317 | structured_req = mock_request_with_structured_output.structured_output_request |
348 | 318 | structured_req.reasoning_ended = False |
349 | 319 | structured_req.structured_output_key = ( |
@@ -372,131 +342,10 @@ def is_reasoning_end_streaming(self, input_ids, delta_ids): |
372 | 342 | new_token_ids=new_token_ids, |
373 | 343 | ) |
374 | 344 |
|
375 | | - # grammar.accept_tokens was called exactly once with the post-marker |
376 | | - # portion of new_token_ids, excluding the reasoning prefix and the |
377 | | - # marker itself. |
378 | | - accept_calls = structured_req.grammar.accept_tokens.call_args_list |
379 | | - assert len(accept_calls) == 1 |
380 | | - _, fed_tokens = accept_calls[0].args |
381 | | - assert fed_tokens == [271, 5005] |
382 | | - |
383 | | - assert structured_req.reasoning_ended is True |
384 | | - # Deferred backend: still return False so the scheduler does not |
385 | | - # also feed full new_token_ids (which would include reasoning). |
386 | | - assert result is False |
387 | | - |
388 | | - def test_should_advance_no_postmarker_skips_grammar_accept( |
389 | | - self, |
390 | | - manager_with_reasoner, |
391 | | - mock_request_with_structured_output, |
392 | | - ): |
393 | | - """When the marker is the last token in new_token_ids, there is no |
394 | | - post-marker tail to drain, so grammar.accept_tokens is not called. |
395 | | - """ |
396 | | - structured_req = mock_request_with_structured_output.structured_output_request |
397 | | - structured_req.reasoning_ended = False |
398 | | - structured_req.structured_output_key = ( |
399 | | - StructuredOutputOptions.JSON_OBJECT, |
400 | | - "{}", |
401 | | - ) |
402 | | - |
403 | | - marker = 248069 |
404 | | - |
405 | | - class MarkerReasoner: |
406 | | - def __init__(self, *_, **__): |
407 | | - pass |
408 | | - |
409 | | - def is_reasoning_end_streaming(self, input_ids, delta_ids): |
410 | | - return marker in list(delta_ids) |
411 | | - |
412 | | - structured_req.reasoner = MarkerReasoner() |
413 | | - |
414 | | - new_token_ids = [9, 198, marker] |
415 | | - mock_request_with_structured_output.all_token_ids = ( |
416 | | - [1, 2, 3] + new_token_ids |
417 | | - ) |
418 | | - |
419 | | - result = manager_with_reasoner.should_advance( |
420 | | - mock_request_with_structured_output, |
421 | | - new_token_ids=new_token_ids, |
422 | | - ) |
423 | | - |
424 | 345 | structured_req.grammar.accept_tokens.assert_not_called() |
425 | 346 | assert structured_req.reasoning_ended is True |
426 | | - assert result is False |
427 | | - |
428 | | - def test_should_advance_structural_tag_with_new_token_ids( |
429 | | - self, |
430 | | - manager_with_reasoner, |
431 | | - mock_request_with_structured_output, |
432 | | - ): |
433 | | - """The structural-tag path uses the exact current-step window.""" |
434 | | - structured_req = mock_request_with_structured_output.structured_output_request |
435 | | - structured_req.reasoning_ended = False |
436 | | - structured_req.structured_output_key = ( |
437 | | - StructuredOutputOptions.STRUCTURAL_TAG, |
438 | | - "{}", |
439 | | - ) |
440 | | - |
441 | | - marker = 248069 |
442 | | - reasoner = MockReasoner(tokenizer=Mock()) |
443 | | - reasoner.is_reasoning_end_streaming = Mock( |
444 | | - side_effect=lambda input_ids, delta_ids: marker in list(delta_ids) |
445 | | - ) |
446 | | - structured_req.reasoner = reasoner |
447 | | - manager_with_reasoner.vllm_config.speculative_config = Mock() |
448 | | - |
449 | | - new_token_ids = [9, marker, 271] |
450 | | - mock_request_with_structured_output.all_token_ids = ( |
451 | | - [1, 2, 3] + new_token_ids |
452 | | - ) |
453 | | - |
454 | | - result = manager_with_reasoner.should_advance( |
455 | | - mock_request_with_structured_output, |
456 | | - new_token_ids=new_token_ids, |
457 | | - ) |
458 | | - |
459 | 347 | assert result is True |
460 | | - assert structured_req.reasoning_end_token_index == 4 |
| 348 | + assert structured_req.reasoning_end_token_index == 5 |
461 | 349 | assert manager_with_reasoner.trim_reasoning_for_advance( |
462 | 350 | mock_request_with_structured_output, new_token_ids |
463 | | - ) == [271] |
464 | | - |
465 | | - def test_should_advance_tolerates_rejected_post_marker_tokens( |
466 | | - self, |
467 | | - manager_with_reasoner, |
468 | | - mock_request_with_structured_output, |
469 | | - ): |
470 | | - """A rejected speculative tail must not fail the request.""" |
471 | | - structured_req = mock_request_with_structured_output.structured_output_request |
472 | | - structured_req.reasoning_ended = False |
473 | | - structured_req.structured_output_key = ( |
474 | | - StructuredOutputOptions.JSON_OBJECT, |
475 | | - "{}", |
476 | | - ) |
477 | | - structured_req.grammar.accept_tokens.return_value = False |
478 | | - |
479 | | - marker = 248069 |
480 | | - reasoner = MockReasoner(tokenizer=Mock()) |
481 | | - reasoner.is_reasoning_end_streaming = Mock( |
482 | | - side_effect=lambda input_ids, delta_ids: marker in list(delta_ids) |
483 | | - ) |
484 | | - structured_req.reasoner = reasoner |
485 | | - |
486 | | - new_token_ids = [9, marker, 271] |
487 | | - mock_request_with_structured_output.all_token_ids = ( |
488 | | - [1, 2, 3] + new_token_ids |
489 | | - ) |
490 | | - |
491 | | - with patch("vllm.v1.structured_output.logger.warning") as warning: |
492 | | - result = manager_with_reasoner.should_advance( |
493 | | - mock_request_with_structured_output, |
494 | | - new_token_ids=new_token_ids, |
495 | | - ) |
496 | | - |
497 | | - assert result is False |
498 | | - assert structured_req.reasoning_ended is True |
499 | | - structured_req.grammar.accept_tokens.assert_called_once_with( |
500 | | - "mock_req", [271] |
501 | | - ) |
502 | | - warning.assert_called_once() |
| 351 | + ) == [271, 5005] |
0 commit comments