[Bugfix] Set stop_sequence explicitly in streaming message_delta event (#55325)

Signed-off-by: Colin McNamara <colin@2cups.com>

Colin McNamara committed Sep 10, 2026 at 17:21 UTC 360f33f61ce02bda04bcb7ea876f1d2e5af8d06d
2 files changed +29 -1
tests/entrypoints/anthropic/test_anthropic_messages_conversion.py
+26
@@ -1610,6 +1610,32 @@ class TestStopSequenceReason:
1610 assert msg_deltas[0]["delta"]["stop_reason"] == "stop_sequence"
1611 assert msg_deltas[0]["delta"]["stop_sequence"] == "</tool>"
1612
1613 + @pytest.mark.asyncio
1614 + async def test_streaming_no_stop_string_emits_explicit_null_stop_sequence(self):
1615 + """exclude_unset=True drops stop_sequence unless it is set explicitly."""
1616 +
1617 + async def sse_input():
1618 + yield _make_stream_chunk(delta=DeltaMessage(role="assistant"))
1619 + yield _make_stream_chunk(delta=DeltaMessage(content="hi"))
1620 + yield _make_stream_chunk(finish_reason="stop")
1621 + yield _make_stream_chunk(
1622 + choices=[],
1623 + usage=UsageInfo(prompt_tokens=5, total_tokens=8, completion_tokens=3),
1624 + )
1625 + yield "data: [DONE]"
1626 +
1627 + converter = _make_stream_converter()
1628 + output = []
1629 + async for event in converter.message_stream_converter(sse_input()):
1630 + output.append(event)
1631 +
1632 + events = _parse_sse_events(output)
1633 + msg_deltas = [data for ev_type, data in events if ev_type == "message_delta"]
1634 + assert len(msg_deltas) == 1
1635 + assert msg_deltas[0]["delta"]["stop_reason"] == "end_turn"
1636 + assert "stop_sequence" in msg_deltas[0]["delta"]
1637 + assert msg_deltas[0]["delta"]["stop_sequence"] is None
1638 +
1639
1640 # ======================================================================
1641 # Client-caused errors are 4xx, not 500 (Issue #52088)
vllm/entrypoints/anthropic/serving.py
+3 -1
@@ -842,7 +842,9 @@ class AnthropicServingMessages(OpenAIServingChat):
842 stop_delta = AnthropicDelta(
843 stop_reason=self.stop_reason_map.get(
844 finish_reason or "stop"
845 - )
845 + ),
846 + # Set explicitly so exclude_unset=True keeps it.
847 + stop_sequence=None,
848 )
849 chunk = AnthropicStreamEvent(
850 type="message_delta",