feat: add STT transcript echo toggle

This commit is contained in:
devatnull 2026-06-28 16:03:50 +03:00 committed by Teknium
parent 95fc3c6b45
commit bfc5262725
4 changed files with 67 additions and 7 deletions

View file

@ -603,6 +603,7 @@ class GatewayConfig:
# STT settings
stt_enabled: bool = True # Whether to auto-transcribe inbound voice messages
stt_echo_transcripts: bool = True # Whether to echo raw STT transcripts back to the user
# Session isolation in shared chats
group_sessions_per_user: bool = True # Isolate group/channel sessions per participant when user IDs are available
@ -726,6 +727,7 @@ class GatewayConfig:
"always_log_local": self.always_log_local,
"filter_silence_narration": self.filter_silence_narration,
"stt_enabled": self.stt_enabled,
"stt_echo_transcripts": self.stt_echo_transcripts,
"group_sessions_per_user": self.group_sessions_per_user,
"thread_sessions_per_user": self.thread_sessions_per_user,
"max_concurrent_sessions": self.max_concurrent_sessions,
@ -772,6 +774,13 @@ class GatewayConfig:
stt_enabled = data.get("stt_enabled")
if stt_enabled is None:
stt_enabled = data.get("stt", {}).get("enabled") if isinstance(data.get("stt"), dict) else None
stt_echo_transcripts = data.get("stt_echo_transcripts")
if stt_echo_transcripts is None:
stt_echo_transcripts = (
data.get("stt", {}).get("echo_transcripts")
if isinstance(data.get("stt"), dict)
else None
)
group_sessions_per_user = data.get("group_sessions_per_user")
thread_sessions_per_user = data.get("thread_sessions_per_user")
@ -815,6 +824,7 @@ class GatewayConfig:
data.get("filter_silence_narration"), True
),
stt_enabled=_coerce_bool(stt_enabled, True),
stt_echo_transcripts=_coerce_bool(stt_echo_transcripts, True),
group_sessions_per_user=_coerce_bool(group_sessions_per_user, True),
thread_sessions_per_user=_coerce_bool(thread_sessions_per_user, False),
multiplex_profiles=_coerce_bool(multiplex_profiles, False),

View file

@ -10147,10 +10147,11 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
message_text,
audio_paths,
)
# Echo each successful transcript back to the user immediately,
# before the agent loop runs. Lets the user verify STT quality
# in real-time and see the raw whisper output verbatim.
if _successful_transcripts:
# Echo each successful transcript back to the user immediately
# when configured. Lets users verify STT quality in real-time,
# while allowing quiet STT for users who only want the agent to
# receive the transcription.
if _successful_transcripts and self._should_echo_stt_transcripts():
_echo_adapter = self.adapters.get(source.platform)
_echo_meta = self._thread_metadata_for_source(source, self._reply_anchor_for_event(event))
if _echo_adapter:
@ -12638,6 +12639,10 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
return True
def _should_echo_stt_transcripts(self) -> bool:
"""Return whether inbound voice/STT transcripts should be echoed to chat."""
return bool(getattr(self.config, "stt_echo_transcripts", True))
async def _send_voice_reply(self, event: MessageEvent, text: str) -> None:
"""Generate TTS audio and send as a voice message before the text reply."""
import uuid as _uuid
@ -14738,9 +14743,9 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew
enriched_text, successful_transcripts = await self._enrich_message_with_transcription(
text, audio_paths,
)
# Echo raw transcripts back to the user so voice interrupts
# feel identical to fresh voice messages.
if successful_transcripts:
# Echo raw transcripts back to the user when configured so voice
# interrupts feel identical to fresh voice messages.
if successful_transcripts and self._should_echo_stt_transcripts():
echo_adapter = self.adapters.get(source.platform)
echo_meta = {"thread_id": source.thread_id} if source.thread_id else None
if echo_adapter:

View file

@ -2017,6 +2017,10 @@ DEFAULT_CONFIG = {
"stt": {
"enabled": True,
# When true, gateway voice messages are transcribed for the agent and
# the raw transcript is also echoed back to the user as a 🎙️ message.
# Set false to keep STT for the agent while suppressing that user-facing echo.
"echo_transcripts": True,
"provider": "local", # "local" (free, faster-whisper) | "groq" | "openai" (Whisper API) | "mistral" (Voxtral Transcribe) | "elevenlabs" (Scribe)
"local": {
"model": "base", # tiny, base, small, medium, large-v3

View file

@ -0,0 +1,41 @@
from types import SimpleNamespace
from gateway.config import GatewayConfig
from gateway.run import GatewayRunner
def test_stt_echo_transcripts_defaults_on_for_backwards_compatibility():
cfg = GatewayConfig.from_dict({})
assert cfg.stt_enabled is True
assert cfg.stt_echo_transcripts is True
assert cfg.to_dict()["stt_echo_transcripts"] is True
def test_stt_echo_transcripts_can_be_disabled_in_stt_section():
cfg = GatewayConfig.from_dict({"stt": {"enabled": True, "echo_transcripts": False}})
assert cfg.stt_enabled is True
assert cfg.stt_echo_transcripts is False
def test_top_level_stt_echo_transcripts_takes_precedence():
cfg = GatewayConfig.from_dict({
"stt_echo_transcripts": False,
"stt": {"echo_transcripts": True},
})
assert cfg.stt_echo_transcripts is False
def test_gateway_runner_uses_stt_echo_transcripts_flag():
runner = GatewayRunner.__new__(GatewayRunner)
runner.config = SimpleNamespace(stt_echo_transcripts=False)
assert runner._should_echo_stt_transcripts() is False
runner.config = SimpleNamespace(stt_echo_transcripts=True)
assert runner._should_echo_stt_transcripts() is True
runner.config = SimpleNamespace()
assert runner._should_echo_stt_transcripts() is True