fix(assistant): 按住说话点掉回复后,上一轮回复会重现并挡住语音条 - #364
Merged
Merged
Conversation
每一轮按住说话生成自增的 request_id 随 voice.stream.start 发出,服务端会把它 回显到该轮的 voice.dialogue.reply 上;handleMessage 据此认轮次,对不上的直接 丢弃。上一轮被 interrupt 后迟到的回复不会再把已经点掉的气泡弹回来——气泡是一层 全屏 Pressable,会挡住语音条,用户得多点一次才能说话。 只 gate voice.dialogue.reply 这一条:错误信封是 startTurn() 唯一的解套途径, voice.stream.started 是 streamId 的唯一来源,voice.command.result 代表服务端 已提交的写入,按轮次丢弃它们的代价都远大于显示一条过期气泡。request_id 为 null/undefined 时一律放行——后端 model_dump() 把缺省值序列化成 null 而非省略 字段,只判 undefined 会把正常回复误吞。 Co-Authored-By: Claude <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
变更说明
AssistantConversationService(前端)在_startTurn()里给每一轮按住说话生成一个自增的
request_id(ptt-turn-N),随voice.stream.start一起发出。后端_request_id_of()拿到后会存进StreamContext,deliver_reply_text()再把它回显到该轮的
voice.dialogue.reply上——这两处都是既有实现,后端不用改。handleMessage()的voice.dialogue.reply分支据此认轮次:request_id跟当前这一轮对不上的直接丢弃,不再写
replyText。上一轮被interrupt后迟到的回复不会再把已经点掉的气泡弹回来,也就不会再用那层全屏 Pressable 挡住语音条、逼用户
多点一次才能说话。
两个刻意的取舍:
voice.dialogue.reply这一条消息,其余(传输错误信封、voice.stream.started、voice.command.result、voice.tts.*)全部照原路走。错误信封是
_startTurn()里await started唯一的解套途径(后端"A stream is already active"/"Audio frame is empty"这类错误会带上旧流的request_id),voice.stream.started是streamId的唯一来源,voice.command.result代表服务端已提交的写入——按轮次丢弃它们的代价都远大于显示了一条过期气泡。
request_id为null或undefined时一律放行。后端model_dump()把缺省的request_id序列化成 JSONnull而不是省略字段,只判undefined会把这类回复全部误吞。
turnRequestId的赋值紧挨着connection.send(),没有放在_startTurn()开头:connect()(含 2s 定位 race)和requestPermission()(权限弹框)都可能停留数秒,提前换 id 会让这段窗口里仍在跑的上一轮流被当成"别人的"而误丢自己的回复。
测试计划
npm run check(前端,exit 0;lint + prettier + tsc + vitest + jest,68 suites / 647 tests 全绿)voice.dialogue.reply不再写replyText(核心场景);request_id为null的回复仍然正常显示(防"修过头"把正常回复吞掉);request_id的传输错误信封仍能让startTurn()解套并进入 error 态(护栏:防止以后有人把这道判断提到
handleMessage()开头,那样会让按住说话永久卡死)。undefined),重新跑这几条新测试,均按预期失败;改回修复后全部转绿——证明测试确实在验证这次改动。
影响面
改动只落在
AssistantConversationService一个类里,后端和消息契约都没动,连续对话(
AssistantContinuousConversationService)不受影响——它本来就用request_id按轮路由,TTS 侧另有
audio_id守卫。