Vòng đời Voice Overlay (macOS)
Đối tượng: Các bạn đóng góp cho ứng dụng macOS. Mục tiêu: giữ cho voice overlay hoạt động dự đoán được khi wake-word và push-to-talk chồng lấn nhau.
Ý định hiện tại
- Nếu overlay đang hiển thị từ wake-word và người dùng ấn phím tắt, session của phím tắt sẽ kế thừa văn bản hiện có thay vì reset lại. Overlay sẽ giữ nguyên trong khi phím tắt được giữ. Khi người dùng thả ra: gửi nếu có văn bản đã trim, nếu không thì đóng.
- Wake-word đơn thuần vẫn tự động gửi khi im lặng; push-to-talk gửi ngay lập tức khi thả ra.
Đã triển khai (9/12/2025)
- Các overlay session giờ mang theo một token cho mỗi lần capture (wake-word hoặc push-to-talk). Các cập nhật partial/final/send/dismiss/level sẽ bị bỏ qua khi token không khớp, tránh các callback cũ.
- Push-to-talk kế thừa bất kỳ văn bản overlay hiển thị nào làm prefix (nên khi ấn phím tắt trong khi wake overlay đang hiển thị sẽ giữ văn bản và thêm giọng nói mới vào). Nó đợi tối đa 1.5 giây để có transcript cuối cùng trước khi fallback về văn bản hiện tại.
- Chime/overlay logging được ghi ở mức
infotrong các categoryvoicewake.overlay,voicewake.ptt, vàvoicewake.chime(session start, partial, final, send, dismiss, chime reason).
Các bước tiếp theo
- VoiceSessionCoordinator (actor)
- Sở hữu chính xác một
VoiceSessiontại một thời điểm. - API (dựa trên token):
beginWakeCapture,beginPushToTalk,updatePartial,endCapture,cancel,applyCooldown. - Bỏ qua các callback mang token cũ (ngăn các recognizer cũ mở lại overlay).
- Sở hữu chính xác một
- VoiceSession (model)
- Các field:
token,source(wakeWord|pushToTalk), committed/volatile text, chime flags, timers (auto-send, idle),overlayMode(display|editing|sending), cooldown deadline.
- Các field:
- Overlay binding
VoiceSessionPublisher(ObservableObject) phản chiếu session đang hoạt động vào SwiftUI.VoiceWakeOverlayViewchỉ render thông qua publisher; nó không bao giờ thay đổi global singleton trực tiếp.- Các hành động của người dùng trên overlay (
sendNow,dismiss,edit) gọi lại coordinator với session token.
- Unified send path
- Khi
endCapture: nếu văn bản đã trim rỗng → dismiss; nếu không thìperformSend(session:)(phát send chime một lần, forward, dismiss). - Push-to-talk: không delay; wake-word: delay tùy chọn cho auto-send.
- Áp dụng cooldown ngắn cho wake runtime sau khi push-to-talk kết thúc để wake-word không kích hoạt lại ngay lập tức.
- Khi
- Logging
- Coordinator ghi log
.infotrong subsystembot.molt, các categoryvoicewake.overlayvàvoicewake.chime. - Các sự kiện chính:
session_started,adopted_by_push_to_talk,partial,finalized,send,dismiss,cancel,cooldown.
- Coordinator ghi log
Checklist debug
-
Stream log trong khi tái hiện sticky overlay:
sudo log stream --predicate 'subsystem == "bot.molt" AND category CONTAINS "voicewake"' --level info --style compact -
Xác minh chỉ có một session token đang hoạt động; các callback cũ nên bị coordinator bỏ qua.
-
Đảm bảo push-to-talk release luôn gọi
endCapturevới token đang hoạt động; nếu văn bản rỗng, mong đợidismissmà không có chime hoặc send.
Các bước migration (đề xuất)
- Thêm
VoiceSessionCoordinator,VoiceSession, vàVoiceSessionPublisher. - Refactor
VoiceWakeRuntimeđể tạo/cập nhật/kết thúc session thay vì chạm vàoVoiceWakeOverlayControllertrực tiếp. - Refactor
VoicePushToTalkđể kế thừa các session hiện có và gọiendCapturekhi thả ra; áp dụng runtime cooldown. - Kết nối
VoiceWakeOverlayControllervới publisher; xóa các lời gọi trực tiếp từ runtime/PTT. - Thêm integration test cho session adoption, cooldown, và empty-text dismissal.