able-branch-17267
02/18/2026, 10:53 AMrefined-appointment-81829
02/18/2026, 3:52 PMrefined-toddler-89382
02/18/2026, 3:58 PMrefined-appointment-81829
02/18/2026, 3:59 PMrefined-toddler-89382
02/18/2026, 4:02 PMrefined-appointment-81829
02/18/2026, 4:06 PM/dev/null TTS as another way and switch between real TTS and mock TTS.refined-appointment-81829
02/18/2026, 4:08 PMrefined-toddler-89382
02/18/2026, 4:11 PMrefined-toddler-89382
02/18/2026, 4:13 PMrefined-appointment-81829
02/18/2026, 4:13 PMrefined-toddler-89382
02/18/2026, 4:15 PMrefined-toddler-89382
02/18/2026, 4:16 PMrefined-appointment-81829
02/18/2026, 4:21 PMtts_node is called to generate audio, and separately, the transcription_node processes text for transcription output. agent_activity.py:1800-1812
>
> Recommended Approach
>
> The suggestion from CWilson to override the tts_node is the correct approach. You can customize the tts_node method in your Agent subclass to conditionally skip TTS API calls based on a mute flag: agent.py:314-339
>
> Here's how this works:
> 1. Override `tts_node`: Create a custom implementation that checks your mute state before calling TTS
> 2. Return empty audio stream when muted: When muted, return an empty AsyncIterable[rtc.AudioFrame] or None to skip TTS generation agent.py:316-320
> 3. Transcription continues independently: The transcription_node runs separately from tts_node, so transcriptions will still be generated from the LLM text output agent_activity.py:1800-1812
>
> Implementation Pattern
> class MutableAgent(Agent):
> def __init__(self, *args, **kwargs):
> super().__init__(*args, **kwargs)
> self.is_muted = False
>
> async def tts_node(self, text: AsyncIterable[str], model_settings: ModelSettings):
> if self.is_muted:
> # Consume text but don't generate audio
> async def _empty_audio():
> async for _ in text:
> pass # Consume text stream
> return
> return _empty_audio()
>
> # Normal TTS processing
> return await super().tts_node(text, model_settings)
>
> Important Limitations
>
> You cannot resume interrupted TTS audio. Once TTS generation is interrupted (whether by user interruption or muting), the audio generation stops and cannot be resumed from where it left off. agent_activity.py:1823-1828 The framework clears the audio buffer on interruption. agent_activity.py:1826-1828
> If you mute mid-speech, the TTS that was already generated will be lost. When you unmute, you would need to trigger a new response (e.g., using session.generate_reply()) to get the agent to speak again.
>
> Notes
>
> The separation between audio generation (tts_node) and transcription (transcription_node) in the pipeline architecture makes this approach viable. agent_activity.py:1800-1812 Transcriptions are forwarded independently of audio, so muting TTS won't affect transcription delivery. However, the framework doesn't support pausing/resuming TTS generation - it's an all-or-nothing operation per speech turn.
>
> References:
> ⢠https://github.com/livekit/agents/blob/d3e4279e/livekit-agents/livekit/agents/voice/agent_activity.py#L1772-L1796
> ⢠https://github.com/livekit/agents/blob/d3e4279e/livekit-agents/livekit/agents/voice/agent.py#L314-L339refined-appointment-81829
02/18/2026, 4:26 PM```# Start normally (audio enabled)
await session.start(agent=MyAgent(), room=ctx.room)
@ctx.room.local_participant.register_rpc_method("toggle_tts")
async def toggle_tts(data: rtc.RpcInvocationData) -> None:
# Flip TTS output on/off
session.output.set_audio_enabled(
not session.output.audio_enabled
)```
When
audio_enabled is False, the agent stops publishing TTS audio but continues sending text transcripts.refined-appointment-81829
02/18/2026, 4:27 PMrefined-toddler-89382
02/18/2026, 4:31 PMacceptable-leather-47181
02/18/2026, 5:52 PM# Insert custom text processing here
async for frame in Agent.default.tts_node(self, text, model_settings):
# just don't yield frame if mute state is true
yield frameacceptable-leather-47181
02/18/2026, 5:53 PM