fix(qqofficial): prevent leading char loss in streaming buffer - #9444
fix(qqofficial): prevent leading char loss in streaming buffer#9444OWWZO wants to merge 2 commits into
Conversation
Copy stream deltas into an owned buffer instead of holding references to yielded MessageChain/Plain objects. Upstream reuse/mutation dropped the first character(s) on group (and C2C) streaming accumulation. Add regression tests covering reference-mutation, independent deltas, and C2C path.
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
_append_stream_delta, non-Plain components are still appended by reference, so if upstream reuses/mutates those components across deltas you could see similar buffer corruption; consider cloning or otherwise guarding non-Plain components as well if that scenario is possible for QQ streaming. - _append_stream_delta
normalizesPlain.textto""when falsy, which changes behavior forNone` or other falsy values; if those are semantically meaningful in your MessageChain usage, it may be safer to preserve the original value rather than coercing to an empty string.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `_append_stream_delta`, non-Plain components are still appended by reference, so if upstream reuses/mutates those components across deltas you could see similar buffer corruption; consider cloning or otherwise guarding non-Plain components as well if that scenario is possible for QQ streaming.
- _append_stream_delta` normalizes `Plain.text` to `""` when falsy, which changes behavior for `None` or other falsy values; if those are semantically meaningful in your MessageChain usage, it may be safer to preserve the original value rather than coercing to an empty string.
## Individual Comments
### Comment 1
<location path="astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py" line_range="195-196" />
<code_context>
+ type=chain.type,
+ )
+ for comp in chain.chain:
+ if isinstance(comp, Plain):
+ self.send_buffer.chain.append(Plain(text=comp.text or ""))
+ else:
+ self.send_buffer.chain.append(comp)
</code_context>
<issue_to_address>
**question:** Using `comp.text or ""` may change behavior for falsy-but-meaningful values compared to the previous implementation.
Previously, the buffer appended `chain.chain` directly, preserving whatever `Plain.text` contained, including `None` or other falsy-but-intentional values. With `Plain(text=comp.text or "")`, all falsy values are coerced to an empty string, which may change semantics if `Plain.text` can legitimately be `None` or other falsy values. If `None` should always be treated as `""`, this is fine; otherwise, prefer `Plain(text=comp.text)` and rely on the constructor to perform any necessary normalization.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- deepcopy non-Plain components to avoid shared-reference mutation - preserve Plain.text as-is instead of coercing falsy values with or "
addressed in f6e250b:
Tests still pass: |
Copy stream deltas into an owned buffer instead of holding references to yielded MessageChain/Plain objects. Upstream reuse/mutation dropped the first character(s) on group (and C2C) streaming accumulation.
Add regression tests covering reference-mutation, independent deltas, and C2C path.
Fixes QQ Official streaming dropping the first character(s) when accumulating deltas (especially group chat; C2C accumulation uses the same helper).
Root cause / 根因:
send_streaming used send_buffer = chain (reference). Upstream can reuse/mutate the same MessageChain/Plain between yields, so the first char was lost on the next delta.
bug日志截图:



Related: #9440
Modifications / 改动点
astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py
Add _append_stream_delta(): copy Plain text into an owned buffer
Group (non-C2C) and C2C accumulation both use this helper (no more bare reference assign)
tests/test_qqofficial_stream_buffer_copy.py
Regression tests: reference-mutation bug repro, independent deltas, multi-char/\n\n, C2C path, single final group send
This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
修复后的验证截图:


首字不再被吞掉
Checklist / 检查清单
[ x] 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
/ 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。
[x ] 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
/ 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。
[x ] 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in
requirements.txtandpyproject.toml./ 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到
requirements.txt和pyproject.toml文件相应位置。[ x] 😮 My changes do not introduce malicious code.
/ 我的更改没有引入恶意代码。
Summary by Sourcery
Ensure QQ Official streaming accumulates text deltas in an owned buffer to avoid leading-character loss when upstream reuses MessageChain objects.
Bug Fixes:
Enhancements:
Tests: