Перейти до основного вмісту

Життєвий цикл голосового накладання (macOS)

Аудиторія: учасники розробки застосунку macOS. Мета: зберегти передбачувану поведінку голосового накладання, коли перетинаються ключове слово пробудження та push-to-talk.

Поточний задум

  • Якщо накладання вже видиме через ключове слово пробудження і користувач натискає гарячу клавішу, сеанс гарячої клавіші підхоплює наявний текст замість його скидання. Накладання залишається відкритим, доки утримується гаряча клавіша. Коли користувач відпускає її: надіслати, якщо є текст після обрізання пробілів, інакше закрити.
  • Лише ключове слово пробудження, як і раніше, автоматично надсилає після тиші; push-to-talk надсилає одразу після відпускання.

Реалізовано (9 грудня 2025 року)

  • Сеанси накладання тепер мають токен для кожного захоплення (ключове слово пробудження або push-to-talk). Оновлення partial/final/send/dismiss/level відкидаються, якщо токен не збігається, що запобігає застарілим зворотним викликам.
  • Push-to-talk підхоплює будь-який текст видимого накладання як префікс (тож натискання гарячої клавіші, поки накладання пробудження активне, зберігає текст і додає нове мовлення). Він очікує до 1.5 с на фінальну транскрипцію, а потім повертається до поточного тексту.
  • Логування chime/overlay записується на рівні info у категоріях voicewake.overlay, voicewake.ptt і voicewake.chime (початок сеансу, partial, final, send, dismiss, причина chime).

Наступні кроки

  1. VoiceSessionCoordinator (actor)
    • Керує рівно одним VoiceSession одночасно.
    • API (на основі токенів): beginWakeCapture, beginPushToTalk, updatePartial, endCapture, cancel, applyCooldown.
    • Відкидає зворотні виклики, що містять застарілі токени (це не дає старим розпізнавачам знову відкрити накладання).
  2. VoiceSession (model)
    • Поля: token, source (wakeWord|pushToTalk), committed/volatile text, прапорці chime, таймери (auto-send, idle), overlayMode (display|editing|sending), крайній термін cooldown.
  3. Прив’язка накладання
    • VoiceSessionPublisher (ObservableObject) відображає активний сеанс у SwiftUI.
    • VoiceWakeOverlayView рендериться лише через publisher; він ніколи не змінює глобальні singleton безпосередньо.
    • Дії користувача в накладанні (sendNow, dismiss, edit) викликають coordinator із токеном сеансу.
  4. Уніфікований шлях надсилання
    • Під час endCapture: якщо текст після обрізання пробілів порожній → закрити; інакше performSend(session:) (один раз відтворює chime надсилання, пересилає, закриває).
    • Push-to-talk: без затримки; ключове слово пробудження: необов’язкова затримка для автонадсилання.
    • Застосовуйте короткий cooldown до runtime пробудження після завершення push-to-talk, щоб ключове слово пробудження не спрацьовувало знову негайно.
  5. Логування
    • Coordinator записує логи .info у підсистемі ai.openclaw, категорії voicewake.overlay і voicewake.chime.
    • Ключові події: session_started, adopted_by_push_to_talk, partial, finalized, send, dismiss, cancel, cooldown.

Контрольний список для налагодження

  • Передавайте логи під час відтворення завислого накладання:
    sudo log stream --predicate 'subsystem == "ai.openclaw" AND category CONTAINS "voicewake"' --level info --style compact
    
  • Переконайтеся, що активний лише один токен сеансу; coordinator має відкидати застарілі зворотні виклики.
  • Переконайтеся, що відпускання push-to-talk завжди викликає endCapture з активним токеном; якщо текст порожній, очікуйте dismiss без chime або надсилання.

Кроки міграції (рекомендовано)

  1. Додайте VoiceSessionCoordinator, VoiceSession і VoiceSessionPublisher.
  2. Переробіть VoiceWakeRuntime, щоб він створював, оновлював і завершував сеанси замість безпосередньої взаємодії з VoiceWakeOverlayController.
  3. Переробіть VoicePushToTalk, щоб він підхоплював наявні сеанси й викликав endCapture при відпусканні; застосовуйте cooldown runtime.
  4. Підключіть VoiceWakeOverlayController до publisher; приберіть прямі виклики з runtime/PTT.
  5. Додайте інтеграційні тести для підхоплення сеансів, cooldown і закриття за порожнього тексту.