From a19cc9c962cf0c7b16fc6456db256b27b4425e9c Mon Sep 17 00:00:00 2001 From: Dave Horton Date: Thu, 1 May 2025 11:50:40 -0400 Subject: [PATCH] play verb: fix race condition where play canceled immediately after start --- lib/tasks/llm/llms/ultravox_s2s.js | 2 +- lib/tasks/play.js | 11 ++++++++++- lib/utils/ws-requestor.js | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/tasks/llm/llms/ultravox_s2s.js b/lib/tasks/llm/llms/ultravox_s2s.js index 1823c2ac..cc43c14d 100644 --- a/lib/tasks/llm/llms/ultravox_s2s.js +++ b/lib/tasks/llm/llms/ultravox_s2s.js @@ -211,7 +211,7 @@ class TaskLlmUltravox_S2S extends Task { async _onServerEvent(_ep, evt) { let endConversation = false; const type = evt.type; - this.logger.debug({evt}, 'TaskLlmUltravox_S2S:_onServerEvent'); + //this.logger.debug({evt}, 'TaskLlmUltravox_S2S:_onServerEvent'); /* server errors of some sort */ if (type === 'error') { diff --git a/lib/tasks/play.js b/lib/tasks/play.js index 8e2ee956..4d192d96 100644 --- a/lib/tasks/play.js +++ b/lib/tasks/play.js @@ -1,6 +1,7 @@ const Task = require('./task'); const {TaskName, TaskPreconditions} = require('../utils/constants'); const { PlayFileNotFoundError } = require('../utils/error'); +const { performance } = require('perf_hooks'); class TaskPlay extends Task { constructor(logger, opts) { @@ -55,6 +56,7 @@ class TaskPlay extends Task { file = {file: this.url, seekOffset: this.seekOffset}; this.seekOffset = -1; } + this._playbackSent = performance.now(); const result = await ep.play(file); playbackSeconds += parseInt(result.playbackSeconds); playbackMilliseconds += parseInt(result.playbackMilliseconds); @@ -95,7 +97,14 @@ class TaskPlay extends Task { } else { this.notifyStatus({event: 'kill-playback'}); - this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); + const now = performance.now(); + if (this._playbackSent && now - this._playbackSent < 30) { + setTimeout(() => { + this.logger.debug('TaskPlay:kill - sending uuid_break after brief delay due to quick cancel'); + this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); + }, 30); + } + else this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); } } } diff --git a/lib/utils/ws-requestor.js b/lib/utils/ws-requestor.js index ab434540..84ba6d09 100644 --- a/lib/utils/ws-requestor.js +++ b/lib/utils/ws-requestor.js @@ -235,7 +235,7 @@ class WsRequestor extends BaseRequestor { /* send the message */ this.ws.send(JSON.stringify(obj), async() => { - this.logger.debug({obj}, `WsRequestor:request websocket: sent (${url})`); + //this.logger.debug({obj}, `WsRequestor:request websocket: sent (${url})`); // If session:reconnect is waiting for ack, hold here until ack to send queuedMsgs if (this._reconnectPromise) { try {