语音叠加层生命周期(macOS)
受众:macOS 应用贡献者。目标:当唤醒词和按键说话重叠时保持语音叠加层可预测。
当前意图
- 如果叠加层已经因唤醒词而可见,用户按下热键,热键会话会采用现有文本而不是重置它。用户按住热键时叠加层保持可见。当用户释放时:如果有修剪的文本则发送,否则关闭。
- 单独的唤醒词仍然在静默时自动发送;按键说话在释放时立即发送。
已实现(2025年12月9日)
- 叠加层会话现在为每次捕获(唤醒词或按键说话)携带一个令牌。当令牌不匹配时,partial/final/send/dismiss/level 更新被丢弃,避免过时的回调。
- 按键说话采用任何可见的叠加层文本作为前缀(因此在唤醒叠加层可见时按下热键会保留文本并附加新语音)。它最多等待 1.5 秒以获得最终转录,然后回退到当前文本。
- Chime/叠加层日志记录在类别 voicewake.overlay、voicewake.ptt 和 voicewake.chime 中以 info 级别发出(会话启动、partial、final、send、dismiss、chime 原因)。
后续步骤
- VoiceSessionCoordinator(actor)
- 一次只拥有一个 VoiceSession。
- API(基于令牌):beginWakeCapture、beginPushToTalk、updatePartial、endCapture、cancel、applyCooldown。
- 丢弃携带过时令牌的回调(防止旧识别器重新打开叠加层)。
- VoiceSession(模型)
- 字段:token、source(wakeWord|pushToTalk)、已提交/易失文本、chime 标志、计时器(自动发送、空闲)、overlayMode(display|editing|sending)、冷却截止时间。
- 叠加层绑定
- VoiceSessionPublisher(ObservableObject)将活动会话镜像到 SwiftUI。
- VoiceWakeOverlayView 仅通过发布者渲染;它从不直接修改全局单例。
- 叠加层用户操作(sendNow、dismiss、edit)使用会话令牌回调到协调器。
- 统一发送路径
- 在 endCapture 上:如果修剪的文本为空 → 关闭;否则 performSend(session:)(播放一次发送 chime、转发、关闭)。
- 按键说话:无延迟;唤醒词:自动发送的可选延迟。
- 按键说话完成后对唤醒运行时应用短暂冷却,以便唤醒词不会立即重新触发。
- 日志记录
- 协调器在子系统 bot.molt、类别 voicewake.overlay 和 voicewake.chime 中发出 .info 日志。
- 关键事件:session_started、adopted_by_push_to_talk、partial、finalized、send、dismiss、cancel、cooldown。
调试清单
-
重现粘性叠加层时流式传输日志:
sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact -
验证只有一个活动会话令牌;协调器应丢弃过时的回调。
-
确保按键说话释放始终使用活动令牌调用 endCapture;如果文本为空,期望 dismiss 而无 chime 或发送。
迁移步骤(建议)
- 添加 VoiceSessionCoordinator、VoiceSession 和 VoiceSessionPublisher。
- 重构 VoiceWakeRuntime 以创建/更新/结束会话,而不是直接触摸 VoiceWakeOverlayController。
- 重构 VoicePushToTalk 以采用现有会话并在释放时调用 endCapture;应用运行时冷却。
- 将 VoiceWakeOverlayController 连接到发布者;从 runtime/PTT 中删除直接调用。
- 为会话采用、冷却和空文本关闭添加集成测试。