fix(voice): stop server VAD from cancelling half-duplex playback
This commit is contained in:
parent
83108c1b5d
commit
aac66d384e
1 changed files with 26 additions and 1 deletions
|
|
@ -30,6 +30,8 @@ const args = parseArgs({
|
|||
// Text mode: send one typed message instead of opening the microphone,
|
||||
// print the reply, and exit. Useful for smoke-testing the tool loop.
|
||||
text: { type: "string" },
|
||||
// Log every realtime event type as it arrives.
|
||||
debug: { type: "boolean", default: false },
|
||||
},
|
||||
}).values
|
||||
|
||||
|
|
@ -352,10 +354,30 @@ ws.addEventListener("open", () => {
|
|||
type: "session.update",
|
||||
session: { type: "realtime", audio: { input: { transcription: { model: "whisper-1" } } } },
|
||||
})
|
||||
send({
|
||||
type: "session.update",
|
||||
session: {
|
||||
type: "realtime",
|
||||
audio: {
|
||||
input: {
|
||||
turn_detection: {
|
||||
type: "server_vad",
|
||||
// Default 500ms makes the model jump in during natural pauses.
|
||||
silence_duration_ms: 900,
|
||||
// In half-duplex mode the server must never auto-cancel a response
|
||||
// on detected speech: a trailing word or leaked echo would cut the
|
||||
// assistant off mid-sentence. Keypress is the interrupt instead.
|
||||
interrupt_response: args.duplex,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ws.addEventListener("message", (event) => {
|
||||
const data = JSON.parse(String(event.data)) as RealtimeEvent
|
||||
if (args.debug && !data.type?.endsWith(".delta")) console.log(`[debug] ${data.type}`)
|
||||
switch (data.type) {
|
||||
case "session.created":
|
||||
if (!args.text) {
|
||||
|
|
@ -381,7 +403,10 @@ ws.addEventListener("message", (event) => {
|
|||
break
|
||||
}
|
||||
case "input_audio_buffer.speech_started":
|
||||
flushPlayback() // barge-in: stop speaking as soon as the user does
|
||||
// Voice barge-in only makes sense in duplex mode (headphones). In
|
||||
// half-duplex, speech that slips past the mic gate must not kill
|
||||
// playback; keypress is the interrupt.
|
||||
if (args.duplex) flushPlayback()
|
||||
break
|
||||
case "conversation.item.input_audio_transcription.completed":
|
||||
console.log(`\nYou: ${data.transcript ?? ""}`)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue