Skip to content

fix(cli): route voice reply behavior by input origin - #7314

Closed
KeroZelvin wants to merge 1 commit into
NousResearch:mainfrom
KeroZelvin:fix/voice-reply-mode-routing
Closed

fix(cli): route voice reply behavior by input origin#7314
KeroZelvin wants to merge 1 commit into
NousResearch:mainfrom
KeroZelvin:fix/voice-reply-mode-routing

Conversation

@KeroZelvin

Copy link
Copy Markdown
Contributor

This PR makes Hermes treat speech-originated turns differently from typed turns, so voice-specific prompt shaping and TTS reply behavior only apply when the input actually came from voice.

Summary

  • route STT-submitted turns through an explicit input_origin path instead of treating them like typed input
  • only apply voice-mode prompt shortening and TTS reply behavior to actual voice-origin turns
  • add regression coverage for the voice queue payload and typed-vs-voice reply behavior

For Humans:

This PR teaches Hermes to distinguish:

  • text turns typed by the user
    from
  • turns that came from speech-to-text / voice input

Right now, those can get mixed together in the CLI voice flow.

The branch adds an explicit input_origin concept:

  • default is "text"
  • STT-submitted turns are queued as "voice"

Then Hermes uses that signal to decide when to apply voice-specific behavior.

Concretely, it changes 3 things

  1. Voice transcripts are marked as voice
    Instead of queueing a raw transcript string, the voice recorder now queues:
  • (transcript, [], "voice")

That means the main input loop knows “this turn came from speech.”

  1. Voice-only response behavior only applies to actual voice turns
    When a turn is marked "voice", Hermes can:
  • add the concise conversational voice prefix
  • optionally TTS the response back

When a turn is normal typed text, it won’t accidentally inherit that voice-specific behavior.

  1. Adds a config knob for reply behavior
    It introduces:
  • voice.message_reply_mode
    with default:
  • "all"

That supports behavior like:

  • "voice_only" = only speak responses for voice-origin turns
  • "all" = speak for both, if that’s what the user wants

Who benefits

  • People who keep voice mode enabled but still type sometimes
  • Users on mobile / headset / accessibility workflows
  • Anyone using push-to-talk and then switching back to keyboard
  • People who want voice input without every typed prompt being treated like a spoken conversation

Problems it prevents

  • Typed input getting an unwanted “respond briefly, conversationally” voice prompt style
  • Typed input triggering TTS when the user only wanted spoken replies for actual voice turns
  • STT turns being treated like generic text instead of first-class voice interactions
  • A real bug/regression this branch guards against:
    • NameError: input_origin is not defined

Why that matters in practice

Without this routing, “voice mode on” can become too blunt:

  • if you type while voice mode is enabled, Hermes may still behave as if you spoke
  • that can change tone, brevity, and whether it talks back aloud

This PR makes voice mode more predictable:

  • spoken turns get voice handling
  • typed turns stay typed turns

That makes Hermes feel much more natural in hybrid use.

What changed in code terms

From the diff:

  • chat(..., input_origin: str = "text")
  • STT queue now sends ("voice") origin
  • voice prompt prefix only applies when input_origin == "voice"
  • TTS reply decision now checks both:
    • _voice_tts
    • input_origin / message_reply_mode

Tests added/updated

The branch also adds regression coverage for:

  • voice-origin chat not throwing NameError
  • typed input not getting voice prefix while voice mode is on
  • STT queue payload carrying the "voice" origin
  • typed-vs-voice behavior staying separated

Test Plan

  • /home/kerozelvin/.hermes/hermes-agent/venv/bin/python -m pytest tests/cli/test_cli_init.py tests/tools/test_voice_cli_integration.py -q

Notes

  • current upstream has nearby voice/CLI changes, but not this input_origin routing behavior
  • branch was rebased onto current origin/main and replay-verified locally

@KeroZelvin

Copy link
Copy Markdown
Contributor Author

Closing this stale branch version in favor of the rebased draft replacement: #11744

That new draft is rebased onto current main, keeps the same fix scope, and carries forward the focused regression coverage.

@KeroZelvin KeroZelvin closed this Apr 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant