feat(chat): 输入栏增加 Web Speech API 语音输入 - #276
Conversation
桌面 GUI 与桌面浏览器 WebUI 在 Composer 工具栏提供麦克风按钮, 识别结果写入输入框(不自动发送)。手机端 WebUI 不展示内置语音按钮, 优先使用系统输入法语音。
Code Review 意见先说结论:方向和工程规范(双端镜像同步、CI、测试计划)都不错,但存在 1 个高频触发的正确性 bug 和 1 个对现有功能的回归,以及桌面 GUI 端实际可用性的疑问,建议修复/澄清后再合入。 P1 · 自动重启后文本重复(必须修)引擎在说话停顿后会触发 复现:开始听写 → 说 "world" → 停顿几秒(Chrome continuous 模式下必然 end/restart)→ 再说 "again",输入框会变成 修复方向:hook 内部区分首次 start 与 auto-restart,重启时不重放 P2 · 听写会打平输入框中的富文本结构(回归)
P3 · 平台实际可用性与描述不符(请作者澄清)
P4 · 小问题
做得好的地方:GUI/WebUI 双端镜像完全一致并更新了 mirror-manifest;识别结果不自动发送;错误码→i18n 映射、CJK 拼接不加空格、卸载清理、输入禁用时自动停止都考虑到了。 |
|
已根据本轮 Code Review 更新,最新提交:
说明:当前没有实体 Mac 做端到端录音验证;macOS 修复基于 Tauri/Wry 的权限配置,并由配置测试和双端构建验证。运行时仍会按 WebView 是否实际暴露 SpeechRecognition 做能力门控。 |
coder-hhx
left a comment
There was a problem hiding this comment.
复核最新提交 5735e6e7:上轮提到的自动重启重复文本、富文本结构被打平等问题已经修复;但目前仍有两项运行时正确性问题,建议修复后再合入。
P1 · 听写会跨会话串入新输入框
crates/agent-gui/src/pages/chat/components/ChatComposerBar.tsx:313(WebUI 镜像为 crates/agent-gateway/web/src/pages/chat/ChatComposerBar.tsx:373)只在 isInputDisabled 时取消听写;但新建/切换会话时 ChatComposerBar 不会卸载,相关路径只是缓存并 clear() / setDraft() 同一个 composer,而且输入通常仍然可用。
因此:在会话 A 开始听写并产生 committed 文本后切到会话 B,recognition session 仍保持 active;旧 voice span 已被清空/替换,但下一次 onresult 会在 B 中重新创建 span,并写入 A 会话累计的完整 transcript。语音内容会跨会话泄漏,存在误发到错误对话的风险。
请把语音 session 生命周期绑定到当前会话标识,并在 draft 缓存、清空或恢复之前完成/取消听写;GUI 与 WebUI 都需要处理,并建议补一个“听写中切换/新建会话”的回归测试。
P2 · 停止听写后光标没有移动到语音文本末尾
crates/agent-gui/src/pages/chat/components/ChatComposerBar.tsx:275(WebUI 为 :334)先 setVoiceTranscript(null),随后只调用普通 focus()。MentionComposer 会把 voice span 替换为文本节点并 normalize(),但 focus() 不会把 selection 放到新增语音文本之后。
WKWebView 复现:输入 Hello → 听写追加 world → 点击麦克风停止 → 输入 !,结果会在旧光标处得到 Hello! world,而不是预期的 Hello world!。请增加“停止听写后聚焦到 voice segment 末尾”的明确路径;如果用户在听写期间已经主动在其他位置编辑,则应保留其当前 selection。
验证:该 head 的 15 个 voice 测试、GUI/WebUI mirror check 和现有 CI 均通过;现有测试没有覆盖上述会话切换和真实 selection 行为。
|
This PR has been inactive for 14 days and is marked stale. It will be closed in 14 days if there is no further activity. Push an update or comment to keep it open. |
摘要
SpeechRecognition/webkitSpeechRecognition)。浏览器或操作系统可能调用远程识别服务,不承诺音频仅在本地处理。lib/voice实现,并保持 mirror-manifest 同步。实现说明
lib/voice:Web Speech API 封装、可测试的 recognition session controller 与useSpeechInputhook。onStart,已提交文本在 pause/end 重启后继续累积;致命错误不会无限重启。NSMicrophoneUsageDescription、NSSpeechRecognitionUsageDescription与com.apple.security.device.audio-inputentitlement,开发/发布配置均启用 hardened runtime entitlement。已知限制
http://<内网 IP>访问属于 insecure context,内置语音入口不可用;需使用 HTTPS/localhost。测试
pnpm buildpnpm buildnode scripts/check-mirror.mjs