Skip to content

qwen3 whitespace around </think> differs between streaming and non-streaming #88

Open

Description

@gdevenyi

Non-streaming strips content after </think> at both ends, and strips reasoning at both ends. Streaming emits the "\n\n" that follows </think> as a content delta, and emits the newline before </think> as reasoning whenever that newline arrives as its own token. So joined streamed content is "\n\nAnswer" where non-streaming returns "Answer".

p = ReasoningParser("qwen3", force_reasoning=True); out = [p.parse_stream_chunk(t) for t in ["r", "\n", "</think>", "\n\n", "Answer"]]
print(out)                                                                            # ('\n', '') ... ('', '\n\n')
print(ReasoningParser("qwen3", force_reasoning=True).parse_non_stream("r\n</think>\n\nAnswer"))  # ('r\n', 'Answer') before generation's strip

Fixed on 5080deploy by 362719c (merged in 1542d52); stream/non-stream parity tests are in tests/server/test_streaming_model_matrix.py.
Seen live on the previous deploy (8eb353b): with thinking on, every streamed answer and tool-call turn starts with a "\n\n" content delta that the non-streaming response does not have (3 of 3 thinking-on turns in a greedy stream-vs-non-stream check).

Activity

  1. gdevenyi commented on Oct 16, 2026

    @gdevenyi
    OwnerAuthor

    Upstream: FlashML-org#712

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions