Repository navigation
fix(openai_api_compatible): strip think tags split across stream chunks - #4066
Merged
crazywoola merged 2 commits intoOct 26, 2026
Merged
crazywoola merged 2 commits into
crazywoola merged 2 commits into
Conversation
Member
|
Hi @gakugaku, thanks for fixing Heads-up about Thanks again! 🙏 |
This was referenced Oct 24, 2026
When thinking mode is disabled, the stream filter only detected a leading <think> if it arrived at the start of a single chunk. If the tag was split, e.g. "<" + "think>", the reasoning was passed through to the answer. Hold a chunk that is a prefix of "<think>" until the tag can be decided, and skip whitespace after </think> that arrives in later chunks.
gakugaku
force-pushed
the
fix/openai-compatible-split-think-stream
branch
from
October 24, 2026 05:57
944d71f to
0a18cf6
Compare
Contributor
Author
|
Rebased on |
PeihuaChen
added a commit
to PeihuaChen/dify-official-plugins
that referenced
this pull request
Oct 26, 2026
…correctly in multimodal rerank - Send an image query as a ScoreMultiModalParam (image_url) instead of inlining "[Image: <base64>]" into the text query, which overflowed the model context window (HTTP 400 from vLLM Qwen3-VL-Reranker). - Wrap raw base64 images (as passed by Dify) into data URIs so they are no longer dropped by URL validation and sent as empty documents. - Raise InvokeBadRequestError for an invalid image query. - Bump version to 0.0.71 (0.0.69 is on main, 0.0.70 is claimed by langgenius#4066).
crazywoola
previously approved these changes
Oct 26, 2026
crazywoola
deployed
to
models/openai_api_compatible
October 26, 2026 03:36 — with
GitHub Actions
Active
crazywoola
pushed a commit
that referenced
this pull request
Oct 26, 2026
…correctly in multimodal rerank (#4079) - Send an image query as a ScoreMultiModalParam (image_url) instead of inlining "[Image: <base64>]" into the text query, which overflowed the model context window (HTTP 400 from vLLM Qwen3-VL-Reranker). - Wrap raw base64 images (as passed by Dify) into data URIs so they are no longer dropped by URL validation and sent as empty documents. - Raise InvokeBadRequestError for an invalid image query. - Bump version to 0.0.71 (0.0.69 is on main, 0.0.70 is claimed by #4066).
crazywoola
approved these changes
Oct 26, 2026
crazywoola
deployed
to
models/openai_api_compatible
October 26, 2026 03:41 — with
GitHub Actions
Active
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When thinking mode is disabled for a model, the plugin removes a leading
<think>…</think>block from the response. In streaming mode this only works if<think>arrives at the start of a single chunk. If the tag is split across chunks (for example<followed bythink>),_filter_thinking_streamsees that the buffer does not start with<think>, yields the chunk and drops the buffer, so the whole reasoning is passed through to the answer.This happens with gateways that stream reasoning inline in
content, e.g. LiteLLM in front of a model that emits<think>in its text. Models that return reasoning inreasoning_contentare not affected, because the plugin builds the<think>tag in one chunk.<think>is held until the tag can be decided. If the stream ends first, the held content is yielded as is.</think>is now skipped even when it arrives in later chunks, matching the non-streaming filter (<think>.*?</think>\s*).finish_reasonand usage.As before, a
<think>block is detected at any chunk boundary, not only at the start of the stream, which matches the non-streaming filter that removes every block.Release Notes
<think>tag was split across chunks.Change Type
Screenshots / Videos
Streamed
contentchunks with thinking mode disabled:<,think,>,reasoning,</think>,\n\nanswer<think>reasoning</think>\n\nansweranswer<think>,reasoning,</think>,\n\nanswer\n\nansweranswerLLM Plugin Checklist
Areas affected by this change (check all that apply)
Version
versioninmanifest.yaml(not the one undermeta)dify_plugin>=0.3.0,<0.11.0is declared inpyproject.tomland locked inuv.lock(or kept inrequirements.txtfor legacy plugins withoutuv.lock) - SDK docsTesting
Added
tests/test_filter_thinking_stream.pyfor split opening and closing tags, whitespace-only chunks after</think>, consecutive thinking blocks, content that only looks like the tag (<b>), a partial tag before the final chunk and at the end of the stream, and an unclosed thinking block. All tests of the plugin pass locally (uv run --frozen --with pytest python -m pytest).Also ran the plugin against a LiteLLM gateway (streaming,
agent_thought_support: not_supported) with a model that returns reasoning inline incontent. The first two chunks were<thandink>The. The same stream gave:<think>The user asks ... Probably just "2".</think>22