Skip to content

Commit 8abb096

Browse files
authored
console: disable agent audio I/O in text mode (#845)
1 parent 16f3e2d commit 8abb096

4 files changed

Lines changed: 56 additions & 3 deletions

File tree

cmd/lk/console_tui.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ func (m consoleModel) Init() tea.Cmd {
151151
}
152152
if m.textMode {
153153
cmds = append(cmds, textinput.Blink)
154+
m.applyTextMode(true)
154155
}
155156
return tea.Batch(cmds...)
156157
}
@@ -216,6 +217,7 @@ func (m consoleModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
216217
m.showShortcuts = false
217218
m.partialTranscript = ""
218219
m.textInput.Focus()
220+
m.applyTextMode(true)
219221
return m, textinput.Blink
220222
case "?":
221223
m.showShortcuts = !m.showShortcuts
@@ -259,6 +261,7 @@ func (m consoleModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
259261
m.audioError = ""
260262
m.inputDev = "Default Input"
261263
m.outputDev = "Default Output"
264+
m.applyTextMode(false)
262265
}
263266
return m, nil
264267

@@ -289,6 +292,7 @@ func (m *consoleModel) switchToAudio() tea.Cmd {
289292
m.showShortcuts = false
290293
m.textInput.Blur()
291294
m.audioError = ""
295+
m.applyTextMode(false)
292296
return nil
293297
}
294298
// Lazy init audio in a goroutine
@@ -297,6 +301,34 @@ func (m *consoleModel) switchToAudio() tea.Cmd {
297301
}
298302
}
299303

304+
// applyTextMode pauses the local audio pipeline and asks the agent to
305+
// disable/enable audio I/O so STT/TTS aren't running in text mode.
306+
func (m *consoleModel) applyTextMode(text bool) {
307+
if m.pipeline.HasAudio() {
308+
m.pipeline.SetPaused(text)
309+
}
310+
311+
m.reqCounter++
312+
reqID := fmt.Sprintf("console-io-%d", m.reqCounter)
313+
audioOn := !text
314+
transcriptionOn := !text
315+
req := &agent.SessionRequest{
316+
RequestId: reqID,
317+
Request: &agent.SessionRequest_UpdateIo{
318+
UpdateIo: &agent.SessionRequest_UpdateIO{
319+
Input: &agent.SessionRequest_UpdateIO_Input{
320+
AudioEnabled: &audioOn,
321+
},
322+
Output: &agent.SessionRequest_UpdateIO_Output{
323+
AudioEnabled: &audioOn,
324+
TranscriptionEnabled: &transcriptionOn,
325+
},
326+
},
327+
},
328+
}
329+
go m.pipeline.SendRequest(req)
330+
}
331+
300332
func (m *consoleModel) beginShutdown() tea.Cmd {
301333
m.shuttingDown = true
302334
m.textMode = false

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ require (
1818
github.com/google/go-containerregistry v0.20.6
1919
github.com/google/go-querystring v1.2.0
2020
github.com/joho/godotenv v1.5.1
21-
github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d
21+
github.com/livekit/protocol v1.45.9-0.20260519220930-d0b708548748
2222
github.com/livekit/server-sdk-go/v2 v2.16.2
2323
github.com/mattn/go-isatty v0.0.21
2424
github.com/moby/patternmatcher v0.6.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,8 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT
366366
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
367367
github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8 h1:coWig9fKxdb/nwOaIoGUUAogso12GblAJh/9SA9hcxk=
368368
github.com/livekit/mediatransportutil v0.0.0-20260309115634-0e2e24b36ee8/go.mod h1:RCd46PT+6sEztld6XpkCrG1xskb0u3SqxIjy4G897Ss=
369-
github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d h1:mE0/AjgGnvsF3q0ipiNGe1HZ3CUKUG7Y+zqey/2JrBE=
370-
github.com/livekit/protocol v1.45.9-0.20260508203311-a249893d6a5d/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k=
369+
github.com/livekit/protocol v1.45.9-0.20260519220930-d0b708548748 h1:QXPwHAEWREHebK+NW+zdgz4x/Dw+I4U5WxVeEbZolwo=
370+
github.com/livekit/protocol v1.45.9-0.20260519220930-d0b708548748/go.mod h1:KEPIJ/ZdMFQ9tmmfv/uT9TjQEuEcZupCZBabuRGEC1k=
371371
github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw=
372372
github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk=
373373
github.com/livekit/server-sdk-go/v2 v2.16.2 h1:eQe24cka3X+5zUivezyL72nwtAJTWFXgibeiyJ/Jm+Y=

pkg/console/pipeline.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ type AudioPipeline struct {
7272
mu sync.Mutex
7373
fftBands [NumFFTBands]float64
7474
muted bool
75+
paused bool // true when audio I/O is paused (e.g. text mode); mic frames are not sent to the agent
7576
level float64 // capture level in dB
7677
playing bool // true when outputting real audio (not silence)
7778

@@ -267,6 +268,19 @@ func (p *AudioPipeline) Muted() bool {
267268
return p.muted
268269
}
269270

271+
// SetPaused stops mic frames from being sent to the agent and drops any
272+
// queued playback. The hardware streams keep running so the pipeline can
273+
// resume instantly. Used to keep audio off the wire in text mode.
274+
func (p *AudioPipeline) SetPaused(paused bool) {
275+
p.mu.Lock()
276+
p.paused = paused
277+
p.mu.Unlock()
278+
279+
if paused && p.playbackRing != nil {
280+
p.playbackRing.Reset()
281+
}
282+
}
283+
270284
func (p *AudioPipeline) Level() float64 {
271285
p.mu.Lock()
272286
defer p.mu.Unlock()
@@ -389,6 +403,13 @@ func (p *AudioPipeline) speakerLoop(ctx context.Context) {
389403
}
390404
}
391405

406+
p.mu.Lock()
407+
paused := p.paused
408+
p.mu.Unlock()
409+
if paused {
410+
continue
411+
}
412+
392413
p.computeMetrics(captureBuf)
393414

394415
_ = p.writeMessage(&agent.AgentSessionMessage{

0 commit comments

Comments
 (0)