diff --git a/src/Infrastructure/BotSharp.Abstraction/MLTasks/Utilities/LlmUtility.cs b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Utilities/LlmUtility.cs new file mode 100644 index 000000000..0303f6e25 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/MLTasks/Utilities/LlmUtility.cs @@ -0,0 +1,14 @@ +namespace BotSharp.Abstraction.MLTasks.Utilities; + +public static class LlmUtility +{ + public static string? VerifyModelParameter(string? curVal, string? defaultVal, IEnumerable? options = null) + { + if (options.IsNullOrEmpty()) + { + return curVal.IfNullOrEmptyAs(defaultVal); + } + + return options.Contains(curVal) ? curVal : defaultVal; + } +} diff --git a/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs b/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs index 8dc5b08d2..da91e215f 100644 --- a/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs +++ b/src/Infrastructure/BotSharp.Core/Session/BotSharpRealtimeSession.cs @@ -74,7 +74,7 @@ public async Task DisconnectAsync() return; } - await _websocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None); + await _websocket.CloseAsync(WebSocketCloseStatus.NormalClosure, $"Normal Closure from {nameof(BotSharpRealtimeSession)}-{_sessionOptions?.Provider}", CancellationToken.None); } public void Dispose() diff --git a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs index 9e1df1cd1..8aa5fa312 100644 --- a/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs +++ b/src/Infrastructure/BotSharp.Core/Session/LlmRealtimeSession.cs @@ -111,7 +111,7 @@ public async Task DisconnectAsync() if (_webSocket.State == WebSocketState.Open) { - await _webSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None); + await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, $"Normal Closure from {nameof(LlmRealtimeSession)}-{_sessionOptions?.Provider}", CancellationToken.None); } } diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs index 0d8aa0b3e..44a6195a7 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/ChatStreamMiddleware.cs @@ -10,7 +10,6 @@ public class ChatStreamMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; - private BotSharpRealtimeSession _session; public ChatStreamMiddleware( RequestDelegate next, @@ -40,7 +39,6 @@ public async Task Invoke(HttpContext httpContext) } catch (Exception ex) { - _session?.Dispose(); _logger.LogError(ex, $"Error when connecting Chat stream. ({ex.Message})"); } return; @@ -52,8 +50,7 @@ public async Task Invoke(HttpContext httpContext) private async Task HandleWebSocket(IServiceProvider services, string agentId, string conversationId, WebSocket webSocket) { - _session?.Dispose(); - _session = new BotSharpRealtimeSession(services, webSocket, new ChatSessionOptions + using var session = new BotSharpRealtimeSession(services, webSocket, new ChatSessionOptions { Provider = "BotSharp Chat Stream", BufferSize = 1024 * 32, @@ -72,7 +69,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st convService.SetConversationId(conversationId, []); await convService.GetConversationRecordOrCreateNew(agentId); - await foreach (ChatSessionUpdate update in _session.ReceiveUpdatesAsync(CancellationToken.None)) + await foreach (ChatSessionUpdate update in session.ReceiveUpdatesAsync(CancellationToken.None)) { var receivedText = update?.RawResponse; if (string.IsNullOrEmpty(receivedText)) @@ -87,7 +84,7 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st _logger.LogCritical($"Start chat stream connection for conversation ({conversationId})"); #endif var request = InitRequest(data, conversationId); - await ConnectToModel(hub, webSocket, request?.States); + await ConnectToModel(hub, session, request?.States); } else if (eventType == "media") { @@ -107,17 +104,16 @@ private async Task HandleWebSocket(IServiceProvider services, string agentId, st } convService.SaveStates(); - await _session.DisconnectAsync(); - _session.Dispose(); + await session.DisconnectAsync(); } - private async Task ConnectToModel(IRealtimeHub hub, WebSocket webSocket, List? states = null) + private async Task ConnectToModel(IRealtimeHub hub, BotSharpRealtimeSession session, List? states = null) { await hub.ConnectToModel(responseToUser: async data => { - if (_session != null) + if (session != null) { - await _session.SendEventAsync(data); + await session.SendEventAsync(data); } }, initStates: states); } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Audio/AudioTranscriptionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Audio/AudioTranscriptionProvider.cs index 462d1fb68..006a41b85 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Audio/AudioTranscriptionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Audio/AudioTranscriptionProvider.cs @@ -47,8 +47,8 @@ private AudioTranscriptionOptions PrepareTranscriptionOptions(string? text) temperature = $"{settings.Temperature}"; } - responseFormat = settings?.ResponseFormat != null ? VerifyTranscriptionParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; - granularity = settings?.Granularity != null ? VerifyTranscriptionParameter(granularity, settings.Granularity.Default, settings.Granularity.Options) : null; + responseFormat = settings?.ResponseFormat != null ? LlmUtility.VerifyModelParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; + granularity = settings?.Granularity != null ? LlmUtility.VerifyModelParameter(granularity, settings.Granularity.Default, settings.Granularity.Options) : null; var options = new AudioTranscriptionOptions { @@ -131,14 +131,4 @@ private AudioTimestampGranularities GetGranularity(string input) return temperature; } - - private string? VerifyTranscriptionParameter(string? curVal, string? defaultVal, IEnumerable? options = null) - { - if (options.IsNullOrEmpty()) - { - return curVal.IfNullOrEmptyAs(defaultVal); - } - - return options.Contains(curVal) ? curVal : defaultVal; - } } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs index 28e44a8ee..732cc7a96 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs @@ -61,9 +61,9 @@ public async Task GetImageEdits(Agent agent, RoleDialogModel me var settings = settingsService.GetSetting(Provider, _model)?.Image?.Edit; - size = settings?.Size != null ? VerifyImageParameter(size, settings.Size.Default, settings.Size.Options) : null; - responseFormat = settings?.ResponseFormat != null ? VerifyImageParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; - background = settings?.Background != null ? VerifyImageParameter(background, settings.Background.Default, settings.Background.Options) : null; + size = settings?.Size != null ? LlmUtility.VerifyModelParameter(size, settings.Size.Default, settings.Size.Options) : null; + responseFormat = settings?.ResponseFormat != null ? LlmUtility.VerifyModelParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; + background = settings?.Background != null ? LlmUtility.VerifyModelParameter(background, settings.Background.Default, settings.Background.Options) : null; var options = new ImageEditOptions(); if (!string.IsNullOrEmpty(size)) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Generation.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Generation.cs index 722f50d9c..c1ad30d16 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Generation.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Generation.cs @@ -42,11 +42,11 @@ public async Task GetImageGeneration(Agent agent, RoleDialogMod var settings = settingsService.GetSetting(Provider, _model)?.Image?.Generation; - size = settings?.Size != null ? VerifyImageParameter(size, settings.Size.Default, settings.Size.Options) : null; - quality = settings?.Quality != null ? VerifyImageParameter(quality, settings.Quality.Default, settings.Quality.Options) : null; - style = settings?.Style != null ? VerifyImageParameter(style, settings.Style.Default, settings.Style.Options) : null; - responseFormat = settings?.ResponseFormat != null ? VerifyImageParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; - background = settings?.Background != null ? VerifyImageParameter(background, settings.Background.Default, settings.Background.Options) : null; + size = settings?.Size != null ? LlmUtility.VerifyModelParameter(size, settings.Size.Default, settings.Size.Options) : null; + quality = settings?.Quality != null ? LlmUtility.VerifyModelParameter(quality, settings.Quality.Default, settings.Quality.Options) : null; + style = settings?.Style != null ? LlmUtility.VerifyModelParameter(style, settings.Style.Default, settings.Style.Options) : null; + responseFormat = settings?.ResponseFormat != null ? LlmUtility.VerifyModelParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; + background = settings?.Background != null ? LlmUtility.VerifyModelParameter(background, settings.Background.Default, settings.Background.Options) : null; var options = new ImageGenerationOptions(); if (!string.IsNullOrEmpty(size)) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Variation.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Variation.cs index 2f4efb186..22d8f2a53 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Variation.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Variation.cs @@ -35,8 +35,8 @@ public async Task GetImageVariation(Agent agent, RoleDialogMode var settings = settingsService.GetSetting(Provider, _model)?.Image?.Variation; - size = settings?.Size != null ? VerifyImageParameter(size, settings.Size.Default, settings.Size.Options) : null; - responseFormat = settings?.ResponseFormat != null ? VerifyImageParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; + size = settings?.Size != null ? LlmUtility.VerifyModelParameter(size, settings.Size.Default, settings.Size.Options) : null; + responseFormat = settings?.ResponseFormat != null ? LlmUtility.VerifyModelParameter(responseFormat, settings.ResponseFormat.Default, settings.ResponseFormat.Options) : null; var options = new ImageVariationOptions(); if (!string.IsNullOrEmpty(size)) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.cs index 883c22ab2..919054b1d 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.cs @@ -201,15 +201,5 @@ private int GetImageCount(string count) } return retCount; } - - private string? VerifyImageParameter(string? curVal, string? defaultVal, IEnumerable? options = null) - { - if (options.IsNullOrEmpty()) - { - return curVal.IfNullOrEmptyAs(defaultVal); - } - - return options.Contains(curVal) ? curVal : defaultVal; - } #endregion } diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs index b8c75d29c..b2bafc7ba 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Using.cs @@ -26,6 +26,7 @@ global using BotSharp.Abstraction.Files.Utilities; global using BotSharp.Abstraction.Functions.Models; global using BotSharp.Abstraction.MLTasks.Settings; +global using BotSharp.Abstraction.MLTasks.Utilities; global using BotSharp.Abstraction.Options; global using BotSharp.Abstraction.Realtime; global using BotSharp.Abstraction.Realtime.Models;