fix(chat): normalize qwen38 leaked thinking and whitespace text parts - #145
Open
grapeot wants to merge 5 commits into
Open
fix(chat): normalize qwen38 leaked thinking and whitespace text parts#145grapeot wants to merge 5 commits into
grapeot wants to merge 5 commits into
Conversation
Add Qwen 3.8 27B (NVFP4, 512k context, YaRN) as the last model preset in the model picker. The model is served via SGLang on none:8002 via the qwen38 provider configured in opencode.json.
qwen38 (SGLang) emits text parts with leading/trailing newlines and pure-newline parts, which the plain-Text fallback rendered as visible blank lines. Filter non-renderable text parts in buildAssistantBlocks and trim in markdownText so empty parts collapse like the web/android renderers.
Assistant text parts can carry thinking leaked by the one-shot qwen3 reasoning parser in SGLang: the first think-close token the model emits ends reasoning early, so the remaining thinking plus the real close token land in the content stream. Add a fence-aware normalizedText() that cuts the leaked tail at the last standalone-line close, drops a dangling standalone open, and trims. Applied to assistant rendering, copy/selection text, and block building; user messages stay verbatim (whitespace-only parts are still skipped there, closing the padding gap). Adds 9 unit tests in MessageThinkLeakNormalizationTests; targeted suite is 20/20 green on the iOS simulator.
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.
背景
iOS 客户端用 qwen38(RadixArk/Qwen3.8-27B-NVFP4,SGLang --reasoning-parser qwen3)时,回复出现大量多余空行,且偶见 thinking 内容 / 字面 think 标签泄漏进正文。Web / Android 渲染同一份 server 数据均正常——根因在 iOS 渲染层缺归一化。完整排查记录(含 SGLang reasoning parser 源码交叉验证与数据核验)见 docs/qwen38_rendering_fix.md。
根因
改动
buildAssistantBlocks跳过纯空白 text part;markdownText渲染前 trim。MessageRowView.normalizedText(fence-aware:``` / ~~~ 围栏识别,闭围栏行必须是纯围栏字符且长度不小于开围栏),只作用于 assistant text part:应用于渲染(markdownText)、复制/划词(copyableText)、块构建(buildAssistantBlocks)。用户消息永不归一化,仅修掉纯空白 part 在用户气泡里留下 20pt padding 的缺口。
MessageThinkLeakNormalizationTests,含顺序回归(尾巴里夹字面 open/close 示例块时,仍只保留最后一个 close 之后的内容)。docs/qwen38_rendering_fix.md:排查记录、SGLang 源码交叉验证、最终方案与取舍。验证
xcodebuild build(同一模拟器):BUILD SUCCEEDED。已知取舍
模型若在普通回复里用独立成行标签引用示例 thinking 块(不在代码围栏内),从文本开头到该示例 close 的内容会被切掉——该 case 已明确不在本次 concern 范围;代码围栏内的示例不受影响。
注:本分支不包含
chore(chat): log part classification in loadMessages(排查期临时 debug log,rebase 时已剔除)。