Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/add-agent-function-visibility

This commit is contained in:
Jicheng Lu 2025-08-20 11:30:52 -05:00
commit 0341a0f7a9
3 changed files with 60 additions and 22 deletions

View file

@ -56,8 +56,9 @@ public class ChatStreamMiddleware
_session = new BotSharpRealtimeSession(services, webSocket, new ChatSessionOptions
{
Provider = "BotSharp Chat Stream",
BufferSize = 1024 * 16,
JsonOptions = BotSharpOptions.defaultJsonOptions
BufferSize = 1024 * 32,
JsonOptions = BotSharpOptions.defaultJsonOptions,
Logger = _logger
});
var hub = services.GetRequiredService<IRealtimeHub>();
@ -79,10 +80,13 @@ public class ChatStreamMiddleware
continue;
}
var (eventType, data) = MapEvents(conn, receivedText);
var (eventType, data) = MapEvents(conn, receivedText, conversationId);
if (eventType == "start")
{
var request = InitRequest(data);
#if DEBUG
_logger.LogCritical($"Start chat stream connection for conversation ({conversationId})");
#endif
var request = InitRequest(data, conversationId);
await ConnectToModel(hub, webSocket, request?.States);
}
else if (eventType == "media")
@ -94,6 +98,9 @@ public class ChatStreamMiddleware
}
else if (eventType == "disconnect")
{
#if DEBUG
_logger.LogCritical($"Disconnecting chat stream connection for conversation ({conversationId})");
#endif
await hub.Completer.Disconnect();
break;
}
@ -115,11 +122,20 @@ public class ChatStreamMiddleware
}, initStates: states);
}
private (string, string) MapEvents(RealtimeHubConnection conn, string receivedText)
private (string, string) MapEvents(RealtimeHubConnection conn, string receivedText, string conversationId)
{
var response = JsonSerializer.Deserialize<ChatStreamEventResponse>(receivedText);
var data = response?.Body?.Payload ?? string.Empty;
ChatStreamEventResponse? response = new();
try
{
response = JsonSerializer.Deserialize<ChatStreamEventResponse>(receivedText);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error when deserializing chat stream event response for conversation ({conversationId}) (response: {receivedText?.SubstringMax(30)})");
}
var data = response?.Body?.Payload ?? string.Empty;
switch (response.Event)
{
case "start":
@ -157,14 +173,15 @@ public class ChatStreamMiddleware
});
}
private ChatStreamRequest? InitRequest(string data)
private ChatStreamRequest? InitRequest(string data, string conversationId)
{
try
{
return JsonSerializer.Deserialize<ChatStreamRequest>(data, BotSharpOptions.defaultJsonOptions);
}
catch
catch (Exception ex)
{
_logger.LogError(ex, $"Error when deserializing initial request data for conversation ({conversationId}).");
return null;
}
}

View file

@ -1,9 +1,12 @@
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Abstraction.Options;
using BotSharp.Abstraction.Realtime;
using BotSharp.Abstraction.Realtime.Models;
using BotSharp.Abstraction.Realtime.Models.Session;
using BotSharp.Abstraction.Routing;
using BotSharp.Abstraction.Utilities;
using BotSharp.Core.Session;
using BotSharp.Plugin.Twilio.Interfaces;
using BotSharp.Plugin.Twilio.Models.Stream;
using Microsoft.AspNetCore.Http;
@ -20,8 +23,11 @@ public class TwilioStreamMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<TwilioStreamMiddleware> _logger;
private BotSharpRealtimeSession _session;
public TwilioStreamMiddleware(RequestDelegate next, ILogger<TwilioStreamMiddleware> logger)
public TwilioStreamMiddleware(
RequestDelegate next,
ILogger<TwilioStreamMiddleware> logger)
{
_next = next;
_logger = logger;
@ -46,6 +52,7 @@ public class TwilioStreamMiddleware
}
catch (Exception ex)
{
_session?.Dispose();
_logger.LogError(ex, $"Error in WebSocket communication: {ex.Message} for conversation {conversationId}");
}
return;
@ -57,7 +64,15 @@ public class TwilioStreamMiddleware
private async Task HandleWebSocket(IServiceProvider services, string agentId, string conversationId, WebSocket webSocket)
{
var settings = services.GetRequiredService<RealtimeModelSettings>();
_session?.Dispose();
_session = new BotSharpRealtimeSession(services, webSocket, new ChatSessionOptions
{
Provider = "BotSharp Twilio Stream",
BufferSize = 1024 * 32,
JsonOptions = BotSharpOptions.defaultJsonOptions,
Logger = _logger
});
var hub = services.GetRequiredService<IRealtimeHub>();
var conn = hub.SetHubConnection(conversationId);
conn.CurrentAgentId = agentId;
@ -65,25 +80,21 @@ public class TwilioStreamMiddleware
// load conversation and state
var convService = services.GetRequiredService<IConversationService>();
convService.SetConversationId(conversationId, []);
var hooks = services.GetHooks<ITwilioSessionHook>(agentId);
foreach (var hook in hooks)
{
await hook.OnStreamingStarted(conn);
}
convService.States.Save();
var routing = services.GetRequiredService<IRoutingService>();
routing.Context.Push(agentId);
var buffer = new byte[1024 * 32];
WebSocketReceiveResult result;
do
await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None))
{
Array.Clear(buffer, 0, buffer.Length);
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
string receivedText = Encoding.UTF8.GetString(buffer, 0, result.Count);
var receivedText = update?.RawResponse;
if (string.IsNullOrEmpty(receivedText))
{
continue;
@ -128,10 +139,13 @@ public class TwilioStreamMiddleware
#endif
await hub.Completer.Disconnect();
await HandleUserDisconnected();
break;
}
} while (!result.CloseStatus.HasValue);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
convService.SaveStates();
await _session.DisconnectAsync();
_session.Dispose();
}
private async Task ConnectToModel(IRealtimeHub hub, WebSocket webSocket)

View file

@ -12,8 +12,15 @@
"phone_number": {
"type": "string",
"description": "Phone number transfer to."
},
"transfer_required_by_user": {
"type": "boolean",
"description": "Only set to true if the user initially and directly asks for a representative, otherwise false."
}
},
"required": [ "transition_message", "phone_number" ]
"required": [
"transition_message",
"phone_number"
]
}
}