Merge pull request #995 from hchen2020/master

Model response timeout
This commit is contained in:
Haiping 2025-04-04 14:50:54 -05:00 committed by GitHub
commit f1fe5714f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 42 additions and 5 deletions

View file

@ -1,4 +1,5 @@
using BotSharp.Abstraction.Utilities;
using BotSharp.Core.Infrastructures;
namespace BotSharp.Core.Realtime.Hooks;
@ -29,6 +30,10 @@ public class RealtimeConversationHook : ConversationHookBase, IConversationHook
{
return;
}
// Clear cache to force to rebuild the agent instruction
Utilities.ClearCache();
var routing = _services.GetRequiredService<IRoutingService>();
message.Role = AgentRole.Function;

View file

@ -49,6 +49,9 @@ public class RealtimeHub : IRealtimeHub
{
await _completer.AppenAudioBuffer(_conn.Data);
}
else if (_conn.Event == "user_dtmf_receiving")
{
}
else if (_conn.Event == "user_dtmf_received")
{
await HandleUserDtmfReceived();

View file

@ -144,13 +144,28 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
Action onUserInterrupted)
{
var buffer = new byte[1024 * 32];
WebSocketReceiveResult result;
// Model response timeout
var timeout = 30;
WebSocketReceiveResult? result = default;
do
{
Array.Clear(buffer, 0, buffer.Length);
result = await _webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
var taskWorker = _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var taskTimer = Task.Delay(1000 * timeout);
var completedTask = await Task.WhenAny(taskWorker, taskTimer);
if (completedTask == taskWorker)
{
result = taskWorker.Result;
}
else
{
_logger.LogWarning($"Timeout {timeout} seconds waiting for Model response.");
await TriggerModelInference("Response user immediately");
continue;
}
// Convert received data to text/audio (Twilio sends Base64-encoded audio)
string receivedText = Encoding.UTF8.GetString(buffer, 0, result.Count);
@ -164,6 +179,11 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
if (response.Type == "error")
{
_logger.LogError($"{response.Type}: {receivedText}");
var error = JsonSerializer.Deserialize<ServerEventErrorResponse>(receivedText);
if (error?.Body.Type == "server_error")
{
break;
}
}
else if (response.Type == "session.created")
{
@ -182,7 +202,6 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
_logger.LogInformation($"{response.Type}: {receivedText}");
var data = JsonSerializer.Deserialize<ResponseAudioTranscript>(receivedText);
await Task.Delay(1000);
onModelAudioTranscriptDone(data.Transcript);
}
else if (response.Type == "response.audio.delta")

View file

@ -1,5 +1,4 @@
using BotSharp.Abstraction.Realtime;
using BotSharp.Abstraction.Realtime.Models;
using BotSharp.Plugin.Twilio.Interfaces;
using BotSharp.Plugin.Twilio.Models.Stream;
using Microsoft.AspNetCore.Http;
@ -100,6 +99,7 @@ public class TwilioStreamMiddleware
}
else
{
conn.Event = "user_dtmf_receiving";
conn.KeypadInputBuffer += dtmfResponse.Body.Digit;
}
break;
@ -115,6 +115,7 @@ public class TwilioStreamMiddleware
streamSid = response.StreamSid,
media = new { payload = message }
};
conn.OnModelAudioResponseDone = () =>
new
{
@ -122,12 +123,21 @@ public class TwilioStreamMiddleware
streamSid = response.StreamSid,
mark = new { name = "responsePart" }
};
conn.OnModelUserInterrupted = () =>
new
{
@event = "clear",
streamSid = response.StreamSid
};
/*if (response.Event == "dtmf")
{
// Send a Stop command to Twilio
string stopPlaybackCommand = "{ \"action\": \"stop_playback\" }";
var stopBytes = Encoding.UTF8.GetBytes(stopPlaybackCommand);
webSocket.SendAsync(new ArraySegment<byte>(stopBytes), WebSocketMessageType.Text, true, CancellationToken.None);
}*/
});
}
}