macOS companion app

هم‌پوشانی صوتی

چرخهٔ عمر هم‌پوشان صوتی (macOS)

مخاطب: مشارکت‌کنندگان برنامهٔ macOS. هدف: قابل پیش‌بینی نگه داشتن هم‌پوشان صوتی وقتی واژهٔ بیدارباش و فشار برای صحبت هم‌پوشانی دارند.

هدف فعلی

  • اگر هم‌پوشان از طریق واژهٔ بیدارباش از قبل نمایان باشد و کاربر کلید میانبر را فشار دهد، نشست کلید میانبر به‌جای بازنشانی متن موجود، آن را می‌پذیرد. هم‌پوشان تا زمانی که کلید میانبر نگه داشته شده است باقی می‌ماند. وقتی کاربر رها می‌کند: اگر متن برش‌خورده‌ای وجود داشته باشد ارسال شود، وگرنه بسته شود.
  • واژهٔ بیدارباش به‌تنهایی همچنان هنگام سکوت به‌طور خودکار ارسال می‌کند؛ فشار برای صحبت بلافاصله هنگام رها کردن ارسال می‌کند.

پیاده‌سازی‌شده (۹ دسامبر ۲۰۲۵)

  • نشست‌های هم‌پوشان اکنون برای هر ضبط (واژهٔ بیدارباش یا فشار برای صحبت) یک توکن دارند. به‌روزرسانی‌های جزئی/نهایی/ارسال/بستن/سطح وقتی توکن مطابقت نداشته باشد کنار گذاشته می‌شوند و از callbackهای کهنه جلوگیری می‌شود.
  • فشار برای صحبت هر متن نمایان هم‌پوشان را به‌عنوان پیشوند می‌پذیرد (بنابراین فشار دادن کلید میانبر وقتی هم‌پوشان بیدارباش باز است، متن را نگه می‌دارد و گفتار جدید را به آن اضافه می‌کند). پیش از بازگشت به متن فعلی، تا ۱٫۵ ثانیه برای transcript نهایی منتظر می‌ماند.
  • ثبت گزارش chime/هم‌پوشان با سطح info در دسته‌های voicewake.overlay، voicewake.ptt و voicewake.chime منتشر می‌شود (شروع نشست، جزئی، نهایی، ارسال، بستن، دلیل chime).

گام‌های بعدی

  1. VoiceSessionCoordinator (اکتور)
    • در هر زمان دقیقاً مالک یک VoiceSession است.
    • API (مبتنی بر توکن): beginWakeCapture، beginPushToTalk، updatePartial، endCapture، cancel، applyCooldown.
    • callbackهایی را که توکن‌های کهنه دارند کنار می‌گذارد (از باز کردن دوبارهٔ هم‌پوشان توسط تشخیص‌دهنده‌های قدیمی جلوگیری می‌کند).
  2. VoiceSession (مدل)
    • فیلدها: token، source (wakeWord|pushToTalk)، متن ثبت‌شده/موقت، پرچم‌های chime، تایمرها (ارسال خودکار، بی‌کاری)، overlayMode (display|editing|sending)، مهلت cooldown.
  3. اتصال هم‌پوشان
    • VoiceSessionPublisher (ObservableObject) نشست فعال را در SwiftUI بازتاب می‌دهد.
    • VoiceWakeOverlayView فقط از طریق publisher رندر می‌شود؛ هرگز singletonهای سراسری را مستقیم تغییر نمی‌دهد.
    • کنش‌های کاربر در هم‌پوشان (sendNow، dismiss، edit) با توکن نشست به coordinator برمی‌گردند.
  4. مسیر ارسال یکپارچه
    • در endCapture: اگر متن برش‌خورده خالی باشد → بستن؛ وگرنه performSend(session:) (chime ارسال را یک بار پخش می‌کند، ارسال می‌کند، می‌بندد).
    • فشار برای صحبت: بدون تأخیر؛ واژهٔ بیدارباش: تأخیر اختیاری برای ارسال خودکار.
    • پس از پایان فشار برای صحبت، یک cooldown کوتاه روی runtime بیدارباش اعمال کنید تا واژهٔ بیدارباش بلافاصله دوباره فعال نشود.
  5. ثبت گزارش
    • coordinator گزارش‌های .info را در subsystem 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
    
  • بررسی کنید فقط یک توکن نشست فعال وجود داشته باشد؛ callbackهای کهنه باید توسط coordinator کنار گذاشته شوند.

  • مطمئن شوید رها کردن فشار برای صحبت همیشه endCapture را با توکن فعال فراخوانی می‌کند؛ اگر متن خالی باشد، انتظار dismiss بدون chime یا ارسال را داشته باشید.

گام‌های مهاجرت (پیشنهادی)

  1. VoiceSessionCoordinator، VoiceSession و VoiceSessionPublisher را اضافه کنید.
  2. VoiceWakeRuntime را بازآرایی کنید تا به‌جای دست زدن مستقیم به VoiceWakeOverlayController، نشست‌ها را ایجاد/به‌روزرسانی/پایان دهد.
  3. VoicePushToTalk را بازآرایی کنید تا نشست‌های موجود را بپذیرد و هنگام رها کردن endCapture را فراخوانی کند؛ cooldown مربوط به runtime را اعمال کنید.
  4. VoiceWakeOverlayController را به publisher وصل کنید؛ فراخوانی‌های مستقیم از runtime/PTT را حذف کنید.
  5. آزمون‌های یکپارچه‌سازی برای پذیرش نشست، cooldown و بستن متن خالی اضافه کنید.

مرتبط