mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Replaces the half-duplex per-playback barge monitors with ONE listener that runs for the entire agent turn in continuous voice mode: armed at utterance-submit, disarmed when the turn is fully done (response + TTS finished). Fixes Teknium's live report that voice interruption never works: (a) not while the LLM is generating, (b) not while TTS plays. Root causes: - HALF-DUPLEX GAP: the barge monitor only spawned when TTS playback STARTED (cli.py streaming/whole-file paths, gateway _tts_stream_begin). During LLM generation there was NO microphone listener at all. - PLAYBACK DEAFNESS: the monitor calibrated its VAD noise floor WHILE the speaker was blasting TTS (speaker bleed baked into the floor), then multiplied it by 8x with a 1s strictly-consecutive block requirement — normal speech could rarely reach the trigger, and the 2s grace swallowed early interjections. New model — tools/voice_mode.full_duplex_listen(): - Pre-playback calibration: quiet-room noise floor established at turn start and HELD through playback (never recalibrated against bleed). - Phase-aware trigger: generation = floor x voice.barge_in_threshold_multiplier (new config, default 3.0, justified by synthetic-frame tests); playback = additionally clamped to a 1500-RMS minimum so bleed alone can't trip; 4000-RMS ceiling keeps speech always reachable. - Windowed-majority detection (>=80% of a 300ms window) instead of the strictly-consecutive counter that reset on intra-word energy dips. - Grace on playback ONSET only (voice.barge_in_grace_seconds, default down 2.0 -> 0.5) — suppresses the onset transient, not the mic. - Debug diagnostics at every decision point (calibrated floor, per-window RMS above 50% of trigger, trip/no-trip, grace suppressions) — always logger.debug, mirrored to stderr under HERMES_VOICE_DEBUG=1. Phase behavior (CLI cli.py + tui_gateway/server.py, same model): - generation: speech interrupts the in-flight turn via the SAME seam the typed/Ctrl+C interrupt uses (agent.interrupt()), cuts any pending TTS pipeline so the stale reply never plays, and submits the captured interjection (pre-roll capture, first syllable kept) as the next turn. - playback: cuts TTS (streaming pipeline stop + fallback speak stop events + file player) and submits the capture. - stop phrase honored in BOTH phases: mid-generation 'stop' interrupts the turn AND ends the voice chat (stop everything). - one listener instance spans generation -> playback (no re-arm race); double-arm refused (CLI _voice_fd_active / gateway _fd_listener_active). Gateway specifics: _arm_full_duplex_listener() at _run_prompt_submit turn start and inside _tts_stream_begin; _speak_text_with_barge registers its (stop, done) pair in _fd_speak_pipelines so fallback speaks are cut and tracked; _tts_stream_barge_in_monitor kept as a shim that arms the new listener. Desktop renderer owns its own mic path (voice-barge-in.ts) and is unaffected; if desktop backend-mic mode is used it inherits via the gateway. Tests: full_duplex_listen synthetic-RMS suite (speech-over-bleed trips, bleed alone doesn't, quiet floor held through playback, grace window, multiplier math 3x vs 8x, windowed-majority dips), CLI listener phase tests (generation interrupt seam, playback cut, lifecycle spans phases, double-arm, config forwarding, stop-phrase-mid-generation), gateway generation-phase interrupt + stop-phrase tests. Generation-interrupt test sabotage-verified. |
||
|---|---|---|
| .. | ||
| computer_use | ||
| environments | ||
| neutts_samples | ||
| wakewords | ||
| __init__.py | ||
| ansi_strip.py | ||
| approval.py | ||
| async_delegation.py | ||
| audio_container.py | ||
| binary_extensions.py | ||
| blueprints.py | ||
| browser_camofox.py | ||
| browser_camofox_state.py | ||
| browser_cdp_tool.py | ||
| browser_dialog_tool.py | ||
| browser_supervisor.py | ||
| browser_tool.py | ||
| budget_config.py | ||
| checkpoint_manager.py | ||
| clarify_gateway.py | ||
| clarify_tool.py | ||
| close_terminal_tool.py | ||
| code_execution_tool.py | ||
| computer_use_tool.py | ||
| credential_files.py | ||
| cronjob_tools.py | ||
| daemon_pool.py | ||
| debug_helpers.py | ||
| delegate_tool.py | ||
| delegation_live_log.py | ||
| desktop_ui.py | ||
| discord_tool.py | ||
| env_passthrough.py | ||
| env_probe.py | ||
| fal_common.py | ||
| feishu_doc_tool.py | ||
| feishu_drive_tool.py | ||
| file_operations.py | ||
| file_state.py | ||
| file_tools.py | ||
| focus_pane_tool.py | ||
| fuzzy_match.py | ||
| homeassistant_tool.py | ||
| hook_output_spill.py | ||
| image_generation_tool.py | ||
| image_source.py | ||
| interrupt.py | ||
| kanban_tools.py | ||
| lazy_deps.py | ||
| managed_tool_gateway.py | ||
| mcp_dashboard_oauth.py | ||
| mcp_oauth.py | ||
| mcp_oauth_manager.py | ||
| mcp_stdio_watchdog.py | ||
| mcp_tool.py | ||
| memory_tool.py | ||
| microsoft_graph_auth.py | ||
| microsoft_graph_client.py | ||
| neutts_synth.py | ||
| open_preview_tool.py | ||
| openrouter_client.py | ||
| osv_check.py | ||
| patch_parser.py | ||
| path_security.py | ||
| process_registry.py | ||
| project_tools.py | ||
| read_extract.py | ||
| read_terminal_tool.py | ||
| registry.py | ||
| schema_sanitizer.py | ||
| send_message_tool.py | ||
| session_search_tool.py | ||
| skill_manager_tool.py | ||
| skill_provenance.py | ||
| skill_usage.py | ||
| skills_ast_audit.py | ||
| skills_guard.py | ||
| skills_hub.py | ||
| skills_sync.py | ||
| skills_tool.py | ||
| slash_confirm.py | ||
| terminal_tool.py | ||
| thread_context.py | ||
| threat_patterns.py | ||
| tirith_security.py | ||
| todo_tool.py | ||
| tool_backend_helpers.py | ||
| tool_output_limits.py | ||
| tool_result_storage.py | ||
| tool_search.py | ||
| transcription_tools.py | ||
| tts_streaming.py | ||
| tts_text_normalize.py | ||
| tts_tool.py | ||
| url_safety.py | ||
| video_generation_tool.py | ||
| vision_tools.py | ||
| voice_mode.py | ||
| wake_word.py | ||
| web_tools.py | ||
| website_policy.py | ||
| working_diff.py | ||
| write_approval.py | ||
| x_search_tool.py | ||
| xai_http.py | ||
| xai_video_tools.py | ||
| yuanbao_tools.py | ||