Repository navigation
fix(pipeline): 流式输出时保留发送期装饰(引用 / @) - #10491
td1336065617 wants to merge 6 commits into
Conversation
result_decorate 在流式场景下无法修改 result.chain:respond 阶段直接使用result.async_stream,且流式结果的 chain 恒为空。而 STREAMING_RESULT 分支原本位于「chain 判空」之后,导致装饰决策无处安放 ——这正是开启流式后 reply_with_quote / reply_with_mention 失效的原因。 改为把「本次需要哪些发送期装饰」记录到事件扩展字段,由平台自身的 send_streaming 按需应用,从而不改动共享阶段接口、也不替平台决定发送细节。
读取 result_decorate 暂存的决策,在首个分片插入 Reply / At,取走后清空以保证只应用一次。链中含非 Plain/Image 组件时跳过,与非流式路径保持一致。
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py" line_range="288" />
<code_context>
try:
async for chain in generator:
source = self.message_obj.raw_message
+ self._apply_streaming_decorations(chain)
if not isinstance(source, botpy.message.C2CMessage):
</code_context>
<issue_to_address>
**Stream repeats decorations**
When the upstream reuses a mutable `MessageChain` across chunks, `_apply_streaming_decorations` mutates the generator-owned chain, so retained `At` or `Reply` components are copied into later output by `_append_stream_delta`; the reply repeats the mention or quote.
Copy the chain before adding streaming decorations.
Also at `astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py:263-269`.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and if the decoration state is applied incorrectly, the first streamed message could be sent with a wrong reply target or an unintended @ mention, and that already-delivered message cannot be undone by reverting the change. Future messages would return to the previous behavior after a revert, so the impact is bounded to messages sent while this code is active.
Blocking findings: astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py:288
上游生成器可能跨分片复用同一个 MessageChain;此前直接在传入对象上插入 Reply/At,已插入的组件会被 _append_stream_delta 再次累积,导致重复引用。 改为 deepcopy 后插入并返回,同时调用处接收返回值;不满足装饰条件时原样返回。
|
合理,已按建议改为「复制后再装饰」。 确认问题
已修(
|
与 AstrBotDevs#10489 同一类问题:_apply_streaming_decorations() 使用了 Reply,但 import 行没有它。该分支的 event 文件同样是从 master 重建的,master 的 import 行不含 Reply。 本地按 CI 方式在真实检出上复核:ruff check 全绿、ruff format 全绿、71 项测试通过。
|
感谢贡献。流式输出时跳过该过程是预期的。 |
问题
开启流式输出(
provider_settings.streaming_response = true)后,引用回复与 @ 发送人全部失效:回复不再引用任何人,也不再 @ 人;而插件直接
event.send()发出的消息、以及内置命令(/reset等)仍正常引用。关掉流式后立即恢复,边界非常明确。
关联 issue:#10490
根因
result_decorate在流式场景下根本走不到插入装饰的代码:关键点:流式结果的
result.chain恒为空 —— 正文在result.async_stream里,而
respond阶段对STREAMING_RESULT是直接发送async_stream(respond/stage.py:217-231),完全不读
result.chain。因此在流式路径上,第 131 行就已经返回,后面的
STREAMING_RESULT判断与装饰插入都执行不到。实测证据(
result_decorate.process入口加临时日志):与 #3501 / #6018 的区别
此前有两次尝试被关闭,本 PR 刻意避开它们被卡住的点:
AstrMessageEvent.send_streaming增加参数。评审意见要求「保持共享管道阶段的平台无关性」「确保所有平台子类都明确处理或忽略此新参数」。
思路正确(认识到已流式发出的文本不能重复发),但仍改动了共享阶段的行为。
本 PR 的做法:
result_decorate把「本次是否需要引用 / @」写入event的扩展字段(
set_extra),不修改result.chain、不改变任何函数签名;send_streaming的实现按需读取该扩展字段,在首个分片上应用。这样共享阶段保持平台无关,新增能力对未改动的平台是零影响(它们只是忽略这个扩展字段)。
改动
astrbot/core/pipeline/result_decorate/stage.py_stash_streaming_decorations():把{"quote": bool, "mention": bool}写入event.set_extra("_streaming_decorations", ...);mention与非流式路径一致地排除私聊。STREAMING_RESULT/STREAMING_FINISH的判断移到「chain 判空」之前(这是本次的核心修复)。astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py_apply_streaming_decorations():读取扩展字段并在首个分片插入Reply/At,读取后立即清空,保证只应用一次(后续分片不会重复引用)。
send_streaming的async for chain in generator循环里调用。Plain/Image组件时跳过,与result_decorate的非流式路径保持一致。实测验证
本机部署(AstrBot 4.28.2,
qq_official群聊,开启流式 + 开启引用)实测日志:客户端表现:回复带着引用气泡,同时正文仍然是逐字流式输出。
测试
新增
tests/test_streaming_result_decoration.py(6 个用例):STREAMING_RESULT且chain为空时,result_decorate仍必须记录装饰决策;STREAMING_FINISH同样记录;reply_with_quote/reply_with_mention配置变化;_apply_streaming_decorations:首个分片插入Reply、决策被取走(只应用一次);Plain/Image组件时跳过。说明:测试只断言「消息链被正确装饰」,不涉及各平台如何把
Reply转成其平台参数(例如
qq_official的message_reference由 #10489 负责),因此本 PR 可独立合并与验证。范围与限制
qq_official的send_streaming中应用装饰。其它平台如需支持,用同样的方式读取
_streaming_decorations即可,无需改动共享阶段。mention在qq_official上会拼成官方现行嵌入文本协议<qqbot-at-user id="..." />;AtAll(
qq == "all")对应<qqbot-at-everyone />,官方限定「仅在文字子频道可用」,故跳过。Summary by Sourcery
Restore reply and mention decorations for streaming responses without changing shared pipeline interfaces.
Bug Fixes:
Enhancements:
Tests: