From a537a647cdf0d4b15f94d3b7b27b4b9f6d1bcaaa Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Tue, 11 Mar 2025 17:20:44 -0500 Subject: [PATCH 01/11] politely ending phone call --- .../Functions/HangupPhoneCallFn.cs | 24 +++++++++++++------ .../LlmContexts/HangupPhoneCallArgs.cs | 9 +++++++ .../LlmContexts/LlmContextIn.cs | 4 ++-- .../util-twilio-hangup_phone_call.json | 6 ++++- 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/HangupPhoneCallArgs.cs diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs index f54c1b42..bc1bad36 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs @@ -1,4 +1,7 @@ +using BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts; +using Microsoft.VisualBasic; using Twilio.Rest.Api.V2010.Account; +using Task = System.Threading.Tasks.Task; namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.Functions; @@ -20,6 +23,7 @@ public class HangupPhoneCallFn : IFunctionCallback public async Task Execute(RoleDialogModel message) { + var args = JsonSerializer.Deserialize(message.FunctionArgs); var states = _services.GetRequiredService(); var callSid = states.GetState("twilio_call_sid"); @@ -30,14 +34,20 @@ public class HangupPhoneCallFn : IFunctionCallback return false; } - // Have to find the SID by the phone number - var call = CallResource.Update( - status: CallResource.UpdateStatusEnum.Completed, - pathSid: callSid - ); + message.Content = args.GoodbyeMessage; - message.Content = "The call has ended."; - message.StopCompletion = true; + _ = Task.Run(async () => + { + await Task.Delay(args.GoodbyeMessage.Split(' ').Length * 400); + // Have to find the SID by the phone number + var call = CallResource.Update( + status: CallResource.UpdateStatusEnum.Completed, + pathSid: callSid + ); + + message.Content = "The call has ended."; + message.StopCompletion = true; + }); return true; } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/HangupPhoneCallArgs.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/HangupPhoneCallArgs.cs new file mode 100644 index 00000000..ea2075d9 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/HangupPhoneCallArgs.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts; + +public class HangupPhoneCallArgs +{ + [JsonPropertyName("goodbye_message")] + public string? GoodbyeMessage { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/LlmContextIn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/LlmContextIn.cs index cde85aa9..3f7ea8b6 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/LlmContextIn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/LlmContexts/LlmContextIn.cs @@ -5,8 +5,8 @@ namespace BotSharp.Plugin.Twilio.OutboundPhoneCallHandler.LlmContexts; public class LlmContextIn { [JsonPropertyName("phone_number")] - public string PhoneNumber { get; set; } + public string PhoneNumber { get; set; } = null!; [JsonPropertyName("initial_message")] - public string InitialMessage { get; set; } + public string? InitialMessage { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-twilio-hangup_phone_call.json b/src/Plugins/BotSharp.Plugin.Twilio/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-twilio-hangup_phone_call.json index 76b0e841..773fac23 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-twilio-hangup_phone_call.json +++ b/src/Plugins/BotSharp.Plugin.Twilio/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-twilio-hangup_phone_call.json @@ -5,7 +5,11 @@ "parameters": { "type": "object", "properties": { + "goodbye_message": { + "type": "string", + "description": "A polite closing statement for ending a conversation." + } }, - "required": [] + "required": [ "goodbye_message" ] } } \ No newline at end of file From 7d4d233f7e5c6248802b1891893dea8e0f419f0c Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Tue, 11 Mar 2025 22:01:16 -0500 Subject: [PATCH 02/11] Update timeout for web driver logic to fetch from appSettings --- .../Browsing/Settings/WebBrowsingSettings.cs | 1 + .../BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs | 7 +++++-- .../BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs | 5 +++-- .../UtilFunctions/UtilWebGoToPageFn.cs | 4 ++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index e1ae9a79..4aebc1b3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -4,4 +4,5 @@ public class WebBrowsingSettings { public string Driver { get; set; } = "Playwright"; public bool Headless { get; set; } + public float? DefaultTimeOut { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs index e1041ae6..9e93b2a7 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Browsing.Settings; + namespace BotSharp.Plugin.WebDriver.Functions; public class GoToPageFn : IFunctionCallback @@ -25,7 +27,7 @@ public class GoToPageFn : IFunctionCallback var url = webDriverService.ReplaceToken(args.Url); url = url.Replace("https://https://", "https://"); - + var _webDriver = _services.GetRequiredService(); var result = await _browser.GoToPage(new MessageInfo { AgentId = message.CurrentAgentId, @@ -33,7 +35,8 @@ public class GoToPageFn : IFunctionCallback MessageId = message.MessageId }, new PageActionArgs { - Url = url + Url = url, + Timeout = _webDriver.DefaultTimeOut }); message.Content = result.IsSuccess ? $"Page {url} is open." : $"Page {url} open failed. {result.Message}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs index 92935233..beb412b5 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs @@ -26,7 +26,7 @@ public class OpenBrowserFn : IFunctionCallback var webDriverService = _services.GetRequiredService(); var url = webDriverService.ReplaceToken(args.Url); - + var _webDriver = _services.GetRequiredService(); url = url.Replace("https://https://", "https://"); var msgInfo = new MessageInfo { @@ -40,7 +40,8 @@ public class OpenBrowserFn : IFunctionCallback }); result = await _browser.GoToPage(msgInfo, new PageActionArgs { - Url = url + Url = url, + Timeout = _webDriver.DefaultTimeOut }); if (result.IsSuccess) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs index 675e1ec8..f62a6ae9 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Browsing.Settings; + namespace BotSharp.Plugin.WebDriver.UtilFunctions; public class UtilWebGoToPageFn : IFunctionCallback @@ -21,6 +23,8 @@ public class UtilWebGoToPageFn : IFunctionCallback { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + var _webDriver = _services.GetRequiredService(); + args.Timeout = _webDriver.DefaultTimeOut; args.WaitForNetworkIdle = false; args.WaitTime = 5; args.OpenNewTab = true; From e570672a365d4fee9273fa3be1267067b0d4c76c Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Wed, 12 Mar 2025 08:49:31 -0500 Subject: [PATCH 03/11] Update default timeout to 30sec --- .../Browsing/Settings/WebBrowsingSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index 4aebc1b3..3245a802 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -4,5 +4,6 @@ public class WebBrowsingSettings { public string Driver { get; set; } = "Playwright"; public bool Headless { get; set; } - public float? DefaultTimeOut { get; set; } + // Default timeout in milliseconds + public float DefaultTimeOut { get; set; } = 30000; } From 47d728b68a9c1ae3de179fa54cef64b91d3c20b3 Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Wed, 12 Mar 2025 08:53:38 -0500 Subject: [PATCH 04/11] Update default timeout to lowercasing --- .../Browsing/Settings/WebBrowsingSettings.cs | 2 +- src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs | 2 +- .../BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs | 2 +- .../UtilFunctions/UtilWebGoToPageFn.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index 3245a802..9565e217 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -5,5 +5,5 @@ public class WebBrowsingSettings public string Driver { get; set; } = "Playwright"; public bool Headless { get; set; } // Default timeout in milliseconds - public float DefaultTimeOut { get; set; } = 30000; + public float DefaultTimeout { get; set; } = 30000; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs index 9e93b2a7..fe69f9d1 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs @@ -36,7 +36,7 @@ public class GoToPageFn : IFunctionCallback }, new PageActionArgs { Url = url, - Timeout = _webDriver.DefaultTimeOut + Timeout = _webDriver.DefaultTimeout }); message.Content = result.IsSuccess ? $"Page {url} is open." : $"Page {url} open failed. {result.Message}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs index beb412b5..0beb80fc 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/OpenBrowserFn.cs @@ -41,7 +41,7 @@ public class OpenBrowserFn : IFunctionCallback result = await _browser.GoToPage(msgInfo, new PageActionArgs { Url = url, - Timeout = _webDriver.DefaultTimeOut + Timeout = _webDriver.DefaultTimeout }); if (result.IsSuccess) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs index f62a6ae9..a29af212 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs @@ -24,7 +24,7 @@ public class UtilWebGoToPageFn : IFunctionCallback PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); var _webDriver = _services.GetRequiredService(); - args.Timeout = _webDriver.DefaultTimeOut; + args.Timeout = _webDriver.DefaultTimeout; args.WaitForNetworkIdle = false; args.WaitTime = 5; args.OpenNewTab = true; From 025b0a1d5cf3d187a71b6e5679dafb0c20d38e0c Mon Sep 17 00:00:00 2001 From: "nick.yi" Date: Wed, 12 Mar 2025 22:57:12 +0800 Subject: [PATCH 05/11] optimize input audio transcription --- .../Models/Realtime/RealtimeSessionBody.cs | 6 ++++++ .../Providers/Realtime/RealTimeCompletionProvider.cs | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs index 0d0947e5..ff2bfc3f 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs @@ -72,4 +72,10 @@ public class InputAudioTranscription { [JsonPropertyName("model")] public string Model { get; set; } = null!; + + [JsonPropertyName("language")] + public string Language { get; set; } = "en"; + + [JsonPropertyName("prompt")] + public string Prompt { get; set; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 0d6f76b4..af6f0ad3 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -138,7 +138,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion { result = await _webSocket.ReceiveAsync( new ArraySegment(buffer), CancellationToken.None); - + // Convert received data to text/audio (Twilio sends Base64-encoded audio) string receivedText = Encoding.UTF8.GetString(buffer, 0, result.Count); if (string.IsNullOrEmpty(receivedText)) @@ -319,6 +319,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion InputAudioTranscription = new InputAudioTranscription { Model = "whisper-1", + Language = "en", }, Voice = "alloy", Instructions = instruction, @@ -329,7 +330,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion MaxResponseOutputTokens = 512, TurnDetection = new RealtimeSessionTurnDetection { - Threshold = 0.8f, + Threshold = 0.9f, PrefixPadding = 300, SilenceDuration = 800 } From 85cf382adcde5dee6f289d19ea98822e0524d661 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 12 Mar 2025 10:13:57 -0500 Subject: [PATCH 06/11] refine chat log --- .../Loggers/Services/ILoggerService.cs | 4 +- .../Filters/ConversationLogFilter.cs | 17 ++++ .../Repositories/IBotSharpRepository.cs | 4 +- .../Utilities/DateTimePagination.cs | 6 ++ .../Services/LoggerService.Conversation.cs | 18 +++- .../FileRepository/FileRepository.Log.cs | 45 ++++++--- .../Controllers/ConversationController.cs | 4 +- .../Controllers/LoggerController.cs | 17 ++-- .../Hooks/ChatHubCrontabHook.cs | 18 +--- .../Repository/MongoRepository.Log.cs | 95 ++++++++++++------- 10 files changed, 145 insertions(+), 83 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationLogFilter.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Utilities/DateTimePagination.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Loggers/Services/ILoggerService.cs b/src/Infrastructure/BotSharp.Abstraction/Loggers/Services/ILoggerService.cs index d704b154..564fcf37 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Loggers/Services/ILoggerService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Loggers/Services/ILoggerService.cs @@ -6,8 +6,8 @@ namespace BotSharp.Abstraction.Loggers.Services; public interface ILoggerService { #region Conversation - Task> GetConversationContentLogs(string conversationId); - Task> GetConversationStateLogs(string conversationId); + Task> GetConversationContentLogs(string conversationId, ConversationLogFilter filter); + Task> GetConversationStateLogs(string conversationId, ConversationLogFilter filter); #endregion #region Instruction diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationLogFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationLogFilter.cs new file mode 100644 index 00000000..59cf9d27 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationLogFilter.cs @@ -0,0 +1,17 @@ +namespace BotSharp.Abstraction.Repositories.Filters; + +public class ConversationLogFilter +{ + public int Size { get; set; } = 20; + public DateTime StartTime { get; set; } = DateTime.UtcNow; + + public ConversationLogFilter() + { + + } + + public static ConversationLogFilter Empty() + { + return new(); + } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 5b92d322..9bccb82e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -164,14 +164,14 @@ public interface IBotSharpRepository : IHaveServiceProvider #region Conversation Content Log void SaveConversationContentLog(ContentLogOutputModel log) => throw new NotImplementedException(); - List GetConversationContentLogs(string conversationId) + DateTimePagination GetConversationContentLogs(string conversationId, ConversationLogFilter filter) => throw new NotImplementedException(); #endregion #region Conversation State Log void SaveConversationStateLog(ConversationStateLogModel log) => throw new NotImplementedException(); - List GetConversationStateLogs(string conversationId) + DateTimePagination GetConversationStateLogs(string conversationId, ConversationLogFilter filter) => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/DateTimePagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/DateTimePagination.cs new file mode 100644 index 00000000..fc56e16e --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/DateTimePagination.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Utilities; + +public class DateTimePagination : PagedItems +{ + public DateTime? NextTime { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Conversation.cs b/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Conversation.cs index 5ce057c3..c424d237 100644 --- a/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Loggers/Services/LoggerService.Conversation.cs @@ -4,18 +4,28 @@ namespace BotSharp.Core.Loggers.Services; public partial class LoggerService { - public async Task> GetConversationContentLogs(string conversationId) + public async Task> GetConversationContentLogs(string conversationId, ConversationLogFilter filter) { + if (filter == null) + { + filter = ConversationLogFilter.Empty(); + } + var db = _services.GetRequiredService(); - var logs = db.GetConversationContentLogs(conversationId); + var logs = db.GetConversationContentLogs(conversationId, filter); return await Task.FromResult(logs); } - public async Task> GetConversationStateLogs(string conversationId) + public async Task> GetConversationStateLogs(string conversationId, ConversationLogFilter filter) { + if (filter == null) + { + filter = ConversationLogFilter.Empty(); + } + var db = _services.GetRequiredService(); - var logs = db.GetConversationStateLogs(conversationId); + var logs = db.GetConversationStateLogs(conversationId, filter); return await Task.FromResult(logs); } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs index 78027987..3913a404 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Log.cs @@ -1,4 +1,5 @@ using BotSharp.Abstraction.Loggers.Models; +using Microsoft.IdentityModel.Logging; using System.IO; namespace BotSharp.Core.Repository @@ -54,26 +55,34 @@ namespace BotSharp.Core.Repository File.WriteAllText(file, JsonSerializer.Serialize(log, _options)); } - public List GetConversationContentLogs(string conversationId) + public DateTimePagination GetConversationContentLogs(string conversationId, ConversationLogFilter filter) { - var logs = new List(); - if (string.IsNullOrEmpty(conversationId)) return logs; + if (string.IsNullOrEmpty(conversationId)) return new(); var convDir = FindConversationDirectory(conversationId); - if (string.IsNullOrEmpty(convDir)) return logs; + if (string.IsNullOrEmpty(convDir)) return new(); var logDir = Path.Combine(convDir, "content_log"); - if (!Directory.Exists(logDir)) return logs; + if (!Directory.Exists(logDir)) return new(); + var logs = new List(); foreach (var file in Directory.GetFiles(logDir)) { var text = File.ReadAllText(file); var log = JsonSerializer.Deserialize(text); - if (log == null) continue; + if (log == null || log.CreatedTime >= filter.StartTime) continue; logs.Add(log); } - return logs.OrderBy(x => x.CreatedTime).ToList(); + + logs = logs.OrderByDescending(x => x.CreatedTime).Take(filter.Size).ToList(); + logs.Reverse(); + return new DateTimePagination + { + Items = logs, + Count = logs.Count, + NextTime = logs.FirstOrDefault()?.CreatedTime + }; } #endregion @@ -99,26 +108,34 @@ namespace BotSharp.Core.Repository File.WriteAllText(file, JsonSerializer.Serialize(log, _options)); } - public List GetConversationStateLogs(string conversationId) + public DateTimePagination GetConversationStateLogs(string conversationId, ConversationLogFilter filter) { - var logs = new List(); - if (string.IsNullOrEmpty(conversationId)) return logs; + if (string.IsNullOrEmpty(conversationId)) return new(); var convDir = FindConversationDirectory(conversationId); - if (string.IsNullOrEmpty(convDir)) return logs; + if (string.IsNullOrEmpty(convDir)) return new(); var logDir = Path.Combine(convDir, "state_log"); - if (!Directory.Exists(logDir)) return logs; + if (!Directory.Exists(logDir)) return new(); + var logs = new List(); foreach (var file in Directory.GetFiles(logDir)) { var text = File.ReadAllText(file); var log = JsonSerializer.Deserialize(text); - if (log == null) continue; + if (log == null || log.CreatedTime >= filter.StartTime) continue; logs.Add(log); } - return logs.OrderBy(x => x.CreatedTime).ToList(); + + logs = logs.OrderByDescending(x => x.CreatedTime).Take(filter.Size).ToList(); + logs.Reverse(); + return new DateTimePagination + { + Items = logs, + Count = logs.Count, + NextTime = logs.FirstOrDefault()?.CreatedTime + }; } #endregion diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index d8b71339..6cb42ac6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -81,11 +81,11 @@ public class ConversationController : ControllerBase } [HttpGet("/conversation/{conversationId}/dialogs")] - public async Task> GetDialogs([FromRoute] string conversationId) + public async Task> GetDialogs([FromRoute] string conversationId, [FromQuery] int count = 100) { var conv = _services.GetRequiredService(); conv.SetConversationId(conversationId, [], isReadOnly: true); - var history = conv.GetDialogHistory(fromBreakpoint: false); + var history = conv.GetDialogHistory(lastCount: count, fromBreakpoint: false); var userService = _services.GetRequiredService(); var agentService = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/LoggerController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/LoggerController.cs index 892c3195..7032bcd3 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/LoggerController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/LoggerController.cs @@ -10,14 +10,11 @@ namespace BotSharp.OpenAPI.Controllers; public class LoggerController : ControllerBase { private readonly IServiceProvider _services; - private readonly IUserIdentity _user; public LoggerController( - IServiceProvider services, - IUserIdentity user) + IServiceProvider services) { _services = services; - _user = user; } [HttpGet("/logger/full-log")] @@ -40,17 +37,21 @@ public class LoggerController : ControllerBase #region Conversation log [HttpGet("/logger/conversation/{conversationId}/content-log")] - public async Task> GetConversationContentLogs([FromRoute] string conversationId) + public async Task> GetConversationContentLogs( + [FromRoute] string conversationId, + [FromQuery] ConversationLogFilter request) { var logging = _services.GetRequiredService(); - return await logging.GetConversationContentLogs(conversationId); + return await logging.GetConversationContentLogs(conversationId, request); } [HttpGet("/logger/conversation/{conversationId}/state-log")] - public async Task> GetConversationStateLogs([FromRoute] string conversationId) + public async Task> GetConversationStateLogs( + [FromRoute] string conversationId, + [FromQuery] ConversationLogFilter request) { var logging = _services.GetRequiredService(); - return await logging.GetConversationStateLogs(conversationId); + return await logging.GetConversationStateLogs(conversationId, request); } #endregion diff --git a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs index f4b4266f..cad0189d 100644 --- a/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs +++ b/src/Plugins/BotSharp.Plugin.ChatHub/Hooks/ChatHubCrontabHook.cs @@ -10,7 +10,6 @@ public class ChatHubCrontabHook : ICrontabHook private readonly IHubContext _chatHub; private readonly ILogger _logger; private readonly IUserIdentity _user; - private readonly IConversationStorage _storage; private readonly BotSharpOptions _options; private readonly ChatHubSettings _settings; @@ -22,7 +21,6 @@ public class ChatHubCrontabHook : ICrontabHook IHubContext chatHub, ILogger logger, IUserIdentity user, - IConversationStorage storage, BotSharpOptions options, ChatHubSettings settings) { @@ -30,7 +28,6 @@ public class ChatHubCrontabHook : ICrontabHook _chatHub = chatHub; _logger = logger; _user = user; - _storage = storage; _options = options; _settings = settings; } @@ -58,19 +55,8 @@ public class ChatHubCrontabHook : ICrontabHook { try { - if (_settings.EventDispatchBy == EventDispatchType.Group) - { - await _chatHub.Clients.Group(item.ConversationId).SendAsync(GENERATE_NOTIFICATION, json); - } - else - { - await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json); - } - } - catch (Exception ex) - { - _logger.LogWarning($"Failed to send event in {nameof(ChatHubCrontabHook)} (conversation id: {item.ConversationId})." + - $"\r\n{ex.Message}\r\n{ex.InnerException}"); + await _chatHub.Clients.User(item.UserId).SendAsync(GENERATE_NOTIFICATION, json); } + catch { } } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Log.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Log.cs index 457faba2..8cd6e158 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Log.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Log.cs @@ -1,5 +1,6 @@ using BotSharp.Abstraction.Loggers.Models; using BotSharp.Abstraction.Repositories.Filters; +using MongoDB.Driver; using System.Text.Json; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -34,7 +35,8 @@ public partial class MongoRepository { if (log == null) return; - var found = _dc.Conversations.AsQueryable().FirstOrDefault(x => x.Id == log.ConversationId); + var filter = Builders.Filter.Eq(x => x.Id, log.ConversationId); + var found = _dc.Conversations.Find(filter).FirstOrDefault(); if (found == null) return; var logDoc = new ConversationContentLogDocument @@ -52,25 +54,36 @@ public partial class MongoRepository _dc.ContentLogs.InsertOne(logDoc); } - public List GetConversationContentLogs(string conversationId) + public DateTimePagination GetConversationContentLogs(string conversationId, ConversationLogFilter filter) { - var logs = _dc.ContentLogs - .AsQueryable() - .Where(x => x.ConversationId == conversationId) - .Select(x => new ContentLogOutputModel - { - ConversationId = x.ConversationId, - MessageId = x.MessageId, - Name = x.Name, - AgentId = x.AgentId, - Role = x.Role, - Source = x.Source, - Content = x.Content, - CreatedTime = x.CreatedTime - }) - .OrderBy(x => x.CreatedTime) - .ToList(); - return logs; + var builder = Builders.Filter; + var logFilters = new List> + { + builder.Eq(x => x.ConversationId, conversationId), + builder.Lt(x => x.CreatedTime, filter.StartTime) + }; + var logSortDef = Builders.Sort.Descending(x => x.CreatedTime); + + var docs = _dc.ContentLogs.Find(builder.And(logFilters)).Sort(logSortDef).Limit(filter.Size).ToList(); + var logs = docs.Select(x => new ContentLogOutputModel + { + ConversationId = x.ConversationId, + MessageId = x.MessageId, + Name = x.Name, + AgentId = x.AgentId, + Role = x.Role, + Source = x.Source, + Content = x.Content, + CreatedTime = x.CreatedTime + }).ToList(); + + logs.Reverse(); + return new DateTimePagination + { + Items = logs, + Count = logs.Count, + NextTime = logs.FirstOrDefault()?.CreatedTime + }; } #endregion @@ -79,7 +92,8 @@ public partial class MongoRepository { if (log == null) return; - var found = _dc.Conversations.AsQueryable().FirstOrDefault(x => x.Id == log.ConversationId); + var filter = Builders.Filter.Eq(x => x.Id, log.ConversationId); + var found = _dc.Conversations.Find(filter).FirstOrDefault(); if (found == null) return; var logDoc = new ConversationStateLogDocument @@ -94,22 +108,33 @@ public partial class MongoRepository _dc.StateLogs.InsertOne(logDoc); } - public List GetConversationStateLogs(string conversationId) + public DateTimePagination GetConversationStateLogs(string conversationId, ConversationLogFilter filter) { - var logs = _dc.StateLogs - .AsQueryable() - .Where(x => x.ConversationId == conversationId) - .Select(x => new ConversationStateLogModel - { - ConversationId = x.ConversationId, - AgentId = x.AgentId, - MessageId = x.MessageId, - States = x.States, - CreatedTime = x.CreatedTime - }) - .OrderBy(x => x.CreatedTime) - .ToList(); - return logs; + var builder = Builders.Filter; + var logFilters = new List> + { + builder.Eq(x => x.ConversationId, conversationId), + builder.Lt(x => x.CreatedTime, filter.StartTime) + }; + var logSortDef = Builders.Sort.Descending(x => x.CreatedTime); + + var docs = _dc.StateLogs.Find(builder.And(logFilters)).Sort(logSortDef).Limit(filter.Size).ToList(); + var logs = docs.Select(x => new ConversationStateLogModel + { + ConversationId = x.ConversationId, + AgentId = x.AgentId, + MessageId = x.MessageId, + States = x.States, + CreatedTime = x.CreatedTime + }).ToList(); + + logs.Reverse(); + return new DateTimePagination + { + Items = logs, + Count = logs.Count, + NextTime = logs.FirstOrDefault()?.CreatedTime + }; } #endregion From 9b6cf7a9ac2d500284fe839322d8ce68947a9d2a Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Wed, 12 Mar 2025 10:51:24 -0500 Subject: [PATCH 07/11] Update settings for IsEnableScreenshot for web driver --- .../Browsing/Settings/WebBrowsingSettings.cs | 1 + .../PlaywrightWebDriver.Screenshot.cs | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index 9565e217..7521a3e4 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -6,4 +6,5 @@ public class WebBrowsingSettings public bool Headless { get; set; } // Default timeout in milliseconds public float DefaultTimeout { get; set; } = 30000; + public bool IsEnableScreenshot { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs index 294c95db..b74af9c4 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.Screenshot.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Browsing.Settings; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver @@ -5,19 +7,21 @@ public partial class PlaywrightWebDriver public async Task ScreenshotAsync(MessageInfo message, string path) { var result = new BrowserActionResult(); - - await _instance.Wait(message.ContextId, waitNetworkIdle: false); - var page = _instance.GetPage(message.ContextId); - - await Task.Delay(300); - var bytes = await page.ScreenshotAsync(new PageScreenshotOptions + var _webDriver = _services.GetRequiredService(); + if (_webDriver.IsEnableScreenshot) { - Path = path, - FullPage = true - }); + await _instance.Wait(message.ContextId, waitNetworkIdle: false); + var page = _instance.GetPage(message.ContextId); + await Task.Delay(300); + var bytes = await page.ScreenshotAsync(new PageScreenshotOptions + { + Path = path, + FullPage = true + }); + result.Body = "data:image/png;base64," + Convert.ToBase64String(bytes); + } result.IsSuccess = true; - result.Body = "data:image/png;base64," + Convert.ToBase64String(bytes); return result; } } From 70eadbd5104b6b1b5a12994cb9fd3437822dac47 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 12 Mar 2025 14:03:38 -0500 Subject: [PATCH 08/11] IRealtimeHook --- .../Conversations/ConversationHookBase.cs | 3 +++ .../Conversations/IConversationHook.cs | 7 ++++++ .../Realtime/IRealtimeHook.cs | 6 +++++ .../BotSharp.Core.Realtime.csproj | 1 + .../Services/RealtimeHub.cs | 21 ++++++++++------ .../Models/Realtime/RealtimeSessionBody.cs | 3 ++- .../Realtime/RealTimeCompletionProvider.cs | 25 +++++++++++++------ .../Functions/HangupPhoneCallFn.cs | 2 +- 8 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs index da87646e..0f115d5d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs @@ -78,4 +78,7 @@ public abstract class ConversationHookBase : IConversationHook public virtual Task OnNotificationGenerated(RoleDialogModel message) => Task.CompletedTask; + + public Task OnUserDisconnected(Conversation conversation) + => Task.CompletedTask; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs index c764f391..f99078ea 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/IConversationHook.cs @@ -25,6 +25,13 @@ public interface IConversationHook /// Task OnUserAgentConnectedInitially(Conversation conversation); + /// + /// Triggered when user disconnects with agent. + /// + /// + /// + Task OnUserDisconnected(Conversation conversation); + /// /// Triggered once for every new conversation. /// diff --git a/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs new file mode 100644 index 00000000..2ad8f1df --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Realtime/IRealtimeHook.cs @@ -0,0 +1,6 @@ +namespace BotSharp.Abstraction.Realtime; + +public interface IRealtimeHook +{ + string[] OnModelTranscriptPrompt(Agent agent); +} diff --git a/src/Infrastructure/BotSharp.Core.Realtime/BotSharp.Core.Realtime.csproj b/src/Infrastructure/BotSharp.Core.Realtime/BotSharp.Core.Realtime.csproj index b29ff39e..c004dc50 100644 --- a/src/Infrastructure/BotSharp.Core.Realtime/BotSharp.Core.Realtime.csproj +++ b/src/Infrastructure/BotSharp.Core.Realtime/BotSharp.Core.Realtime.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs index e8548c4d..0e6027da 100644 --- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs +++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs @@ -1,4 +1,6 @@ using BotSharp.Abstraction.Utilities; +using BotSharp.Core.Infrastructures; +using Microsoft.AspNetCore.Cors.Infrastructure; namespace BotSharp.Core.Realtime.Services; @@ -86,11 +88,14 @@ public class RealtimeHub : IRealtimeHub var routing = _services.GetRequiredService(); routing.Context.Push(agent.Id); + var storage = _services.GetRequiredService(); var dialogs = convService.GetDialogHistory(); if (dialogs.Count == 0) { dialogs.Add(new RoleDialogModel(AgentRole.User, "Hi")); + storage.Append(_conn.ConversationId, dialogs.First()); } + routing.Context.SetDialogs(dialogs); await _completer.Connect(_conn, @@ -155,6 +160,7 @@ public class RealtimeHub : IRealtimeHub { // append output audio transcript to conversation dialogs.Add(message); + storage.Append(_conn.ConversationId, message); foreach (var hook in hookProvider.HooksOrderByPriority) { @@ -174,6 +180,7 @@ public class RealtimeHub : IRealtimeHub { // append input audio transcript to conversation dialogs.Add(message); + storage.Append(_conn.ConversationId, message); foreach (var hook in hookProvider.HooksOrderByPriority) { @@ -224,6 +231,9 @@ public class RealtimeHub : IRealtimeHub }; dialogs.Add(message); + var storage = _services.GetRequiredService(); + storage.Append(_conn.ConversationId, message); + foreach (var hook in hookProvider.HooksOrderByPriority) { hook.SetAgent(agent) @@ -239,14 +249,9 @@ public class RealtimeHub : IRealtimeHub private async Task HandleUserDisconnected() { - // Save dialog history - var routing = _services.GetRequiredService(); - var storage = _services.GetRequiredService(); - var dialogs = routing.Context.GetDialogs(); - foreach (var item in dialogs) - { - storage.Append(_conn.ConversationId, item); - } + var convService = _services.GetRequiredService(); + var conversation = await convService.GetConversation(_conn.ConversationId); + await HookEmitter.Emit(_services, x => x.OnUserDisconnected(conversation)); } private async Task SendEventToUser(WebSocket webSocket, object message) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs index ff2bfc3f..f6a6322c 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs @@ -77,5 +77,6 @@ public class InputAudioTranscription public string Language { get; set; } = "en"; [JsonPropertyName("prompt")] - public string Prompt { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Prompt { get; set; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index af6f0ad3..47a9030d 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -2,7 +2,9 @@ using BotSharp.Abstraction.Conversations.Enums; using BotSharp.Abstraction.Files.Utilities; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Options; +using BotSharp.Abstraction.Realtime; using BotSharp.Abstraction.Realtime.Models; +using BotSharp.Abstraction.Routing; using BotSharp.Core.Infrastructures; using BotSharp.Plugin.OpenAI.Models.Realtime; using OpenAI.Chat; @@ -42,7 +44,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion Action onModelReady, Action onModelAudioDeltaReceived, Action onModelAudioResponseDone, - Action onAudioTranscriptDone, + Action onModelAudioTranscriptDone, Action> onModelResponseDone, Action onConversationItemCreated, Action onInputAudioTranscriptionCompleted, @@ -64,7 +66,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion onModelReady, onModelAudioDeltaReceived, onModelAudioResponseDone, - onAudioTranscriptDone, + onModelAudioTranscriptDone, onModelResponseDone, onConversationItemCreated, onInputAudioTranscriptionCompleted, @@ -125,10 +127,10 @@ public class RealTimeCompletionProvider : IRealTimeCompletion Action onModelReady, Action onModelAudioDeltaReceived, Action onModelAudioResponseDone, - Action onAudioTranscriptDone, + Action onModelAudioTranscriptDone, Action> onModelResponseDone, Action onConversationItemCreated, - Action onInputAudioTranscriptionCompleted, + Action onUserAudioTranscriptionCompleted, Action onUserInterrupted) { var buffer = new byte[1024 * 32]; @@ -171,7 +173,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion _logger.LogInformation($"{response.Type}: {receivedText}"); var data = JsonSerializer.Deserialize(receivedText); await Task.Delay(1000); - onAudioTranscriptDone(data.Transcript); + onModelAudioTranscriptDone(data.Transcript); } else if (response.Type == "response.audio.delta") { @@ -201,8 +203,11 @@ public class RealTimeCompletionProvider : IRealTimeCompletion else if (response.Type == "conversation.item.input_audio_transcription.completed") { _logger.LogInformation($"{response.Type}: {receivedText}"); - var message = await OnInputAudioTranscriptionCompleted(conn, receivedText); - onInputAudioTranscriptionCompleted(message); + var message = await OnUserAudioTranscriptionCompleted(conn, receivedText); + if (!string.IsNullOrEmpty(message.Content)) + { + onUserAudioTranscriptionCompleted(message); + } } else if (response.Type == "input_audio_buffer.speech_started") { @@ -309,6 +314,9 @@ public class RealTimeCompletionProvider : IRealTimeCompletion return fn; }).ToArray(); + var words = new List(); + HookEmitter.Emit(_services, hook => words.AddRange(hook.OnModelTranscriptPrompt(agent))); + var sessionUpdate = new { type = "session.update", @@ -320,6 +328,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion { Model = "whisper-1", Language = "en", + Prompt = string.Join(", ", words.Select(x => x.ToLower().Trim()).Distinct()).SubstringMax(1024) }, Voice = "alloy", Instructions = instruction, @@ -663,7 +672,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion return outputs; } - public async Task OnInputAudioTranscriptionCompleted(RealtimeHubConnection conn, string response) + public async Task OnUserAudioTranscriptionCompleted(RealtimeHubConnection conn, string response) { var data = JsonSerializer.Deserialize(response); return new RoleDialogModel(AgentRole.User, data.Transcript) diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs index bc1bad36..cd755306 100644 --- a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs +++ b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs @@ -45,7 +45,7 @@ public class HangupPhoneCallFn : IFunctionCallback pathSid: callSid ); - message.Content = "The call has ended."; + message.Content = "The call has been ended."; message.StopCompletion = true; }); From 7646030f782b279a353604199022955fa09f935a Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Wed, 12 Mar 2025 16:33:20 -0500 Subject: [PATCH 09/11] add vector storage snapshots --- .../Files/Utilities/FileUtility.cs | 11 ++ .../Knowledges/IKnowledgeService.cs | 8 + .../VectorStorage/IVectorDb.cs | 42 +++-- .../Snapshot/VectorCollectionSnapshot.cs | 9 + .../Controllers/DashboardController.cs | 10 +- .../Controllers/KnowledgeBaseController.cs | 54 +++++- .../{ => Request}/AgentCreationModel.cs | 0 .../{ => Request}/AgentTaskCreateModel.cs | 0 .../{ => Request}/AgentTaskUpdateModel.cs | 0 .../{ => Request}/AgentTemplatePatchModel.cs | 0 .../Agents/{ => Request}/AgentUpdateModel.cs | 0 .../{ => Request}/RoutingRuleUpdateModel.cs | 0 .../Agents/{ => View}/AgentTaskViewModel.cs | 0 .../Agents/{ => View}/AgentViewModel.cs | 0 .../ConversationCreationModel.cs | 0 .../{ => Request}/ConversationSummaryModel.cs | 0 .../{ => Request}/InputMessageFiles.cs | 0 .../MigrateLatestStateRequest.cs | 0 .../{ => Request}/NewMessageModel.cs | 0 .../UpdateConversationRequest.cs | 0 .../UpdateConversationTitleAliasModel.cs | 0 .../UpdateConversationTitleModel.cs | 0 .../{ => Request}/UpdateMessageModel.cs | 0 .../{ => Response}/ChatResponseModel.cs | 0 .../{ => View}/ConversationViewModel.cs | 0 .../{ => Request}/EmbeddingInputModel.cs | 0 .../Files/{ => View}/MessageFileViewModel.cs | 0 .../{ => Request}/InstructBaseRequest.cs | 0 .../{ => Request}/InstructMessageModel.cs | 0 .../{ => View}/ImageGenerationViewModel.cs | 0 .../{ => View}/InstructBaseViewModel.cs | 0 .../{ => View}/InstructionLogViewModel.cs | 0 .../{ => View}/MultiModalViewModel.cs | 0 .../{ => View}/PdfCompletionViewModel.cs | 0 .../{ => View}/SpeechToTextViewModel.cs | 0 .../CreateVectorCollectionRequest.cs | 0 .../DeleteVectorCollectionSnapshotRequest.cs | 9 + .../{ => Request}/GetKnowledgeDocsRequest.cs | 0 .../SearchGraphKnowledgeRequest.cs | 0 .../SearchVectorKnowledgeRequest.cs | 0 .../VectorKnowledgeCreateRequest.cs | 0 .../VectorKnowledgeUpdateRequest.cs | 0 .../VectorKnowledgeUploadRequest.cs | 0 .../{ => View}/GraphKnowledgeViewModel.cs | 0 .../{ => View}/KnowledgeFileViewModel.cs | 0 .../VectorCollectionConfigViewModel.cs | 0 .../View/VectorCollectionSnapshotViewModel.cs | 35 ++++ .../{ => View}/VectorKnowledgeViewModel.cs | 0 .../{ => Request}/RoleAgentActionViewModel.cs | 0 .../Roles/{ => Request}/RoleViewModel.cs | 0 .../Roles/{ => View}/RoleUpdateModel.cs | 0 .../Users/{ => Request}/UserAvatarModel.cs | 0 .../Users/{ => Request}/UserCreationModel.cs | 0 .../{ => Request}/UserResetPasswordModel.cs | 0 .../Users/{ => Request}/UserUpdateModel.cs | 0 .../{ => View}/UserAgentActionViewModel.cs | 0 .../UserDashboardConversationViewModel.cs} | 13 +- .../Users/{ => View}/UserViewModel.cs | 0 src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs | 2 +- .../Services/KnowledgeService.Snapshot.cs | 64 +++++++ .../Models/RecoverFromSnapshotResponse.cs | 15 ++ .../BotSharp.Plugin.Qdrant/QdrantDb.cs | 158 ++++++++++++++++++ 62 files changed, 403 insertions(+), 27 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/Snapshot/VectorCollectionSnapshot.cs rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/AgentCreationModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/AgentTaskCreateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/AgentTaskUpdateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/AgentTemplatePatchModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/AgentUpdateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => Request}/RoutingRuleUpdateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => View}/AgentTaskViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/{ => View}/AgentViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/ConversationCreationModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/ConversationSummaryModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/InputMessageFiles.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/MigrateLatestStateRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/NewMessageModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/UpdateConversationRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/UpdateConversationTitleAliasModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/UpdateConversationTitleModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Request}/UpdateMessageModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => Response}/ChatResponseModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/{ => View}/ConversationViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/{ => Request}/EmbeddingInputModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/{ => View}/MessageFileViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => Request}/InstructBaseRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => Request}/InstructMessageModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/ImageGenerationViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/InstructBaseViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/InstructionLogViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/MultiModalViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/PdfCompletionViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/{ => View}/SpeechToTextViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/CreateVectorCollectionRequest.cs (100%) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/DeleteVectorCollectionSnapshotRequest.cs rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/GetKnowledgeDocsRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/SearchGraphKnowledgeRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/SearchVectorKnowledgeRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/VectorKnowledgeCreateRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/VectorKnowledgeUpdateRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => Request}/VectorKnowledgeUploadRequest.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => View}/GraphKnowledgeViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => View}/KnowledgeFileViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => View}/VectorCollectionConfigViewModel.cs (100%) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionSnapshotViewModel.cs rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/{ => View}/VectorKnowledgeViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/{ => Request}/RoleAgentActionViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/{ => Request}/RoleViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/{ => View}/RoleUpdateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => Request}/UserAvatarModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => Request}/UserCreationModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => Request}/UserResetPasswordModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => Request}/UserUpdateModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => View}/UserAgentActionViewModel.cs (100%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{UserDashboardConversationModel.cs => View/UserDashboardConversationViewModel.cs} (57%) rename src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/{ => View}/UserViewModel.cs (100%) create mode 100644 src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Snapshot.cs create mode 100644 src/Plugins/BotSharp.Plugin.Qdrant/Models/RecoverFromSnapshotResponse.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs b/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs index bf2644dd..db9ccb65 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Files/Utilities/FileUtility.cs @@ -47,6 +47,17 @@ public static class FileUtility return $"data:{contentType};base64,{base64}"; } + public static BinaryData BuildBinaryDataFromFile(IFormFile file) + { + using var stream = new MemoryStream(); + file.CopyTo(stream); + stream.Position = 0; + var binary = BinaryData.FromStream(stream); + stream.Close(); + + return binary; + } + public static string GetFileContentType(string fileName) { string contentType; diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs index 3fc90816..fb368b0b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/IKnowledgeService.cs @@ -60,6 +60,14 @@ public interface IKnowledgeService Task GetKnowledgeDocumentBinaryData(string collectionName, Guid fileId); #endregion + #region Snapshot + Task> GetVectorCollectionSnapshots(string collectionName); + Task CreateVectorCollectionSnapshot(string collectionName); + Task DownloadVectorCollectionSnapshot(string collectionName, string snapshotFileName); + Task RecoverVectorCollectionFromSnapshot(string collectionName, string snapshotFileName, BinaryData snapshotData); + Task DeleteVectorCollectionSnapshot(string collectionName, string snapshotName); + #endregion + #region Common Task RefreshVectorKnowledgeConfigs(VectorCollectionConfigsModel configs); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs index 66f7727a..ff090da6 100644 --- a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/IVectorDb.cs @@ -6,14 +6,36 @@ public interface IVectorDb { string Provider { get; } - Task DoesCollectionExist(string collectionName); - Task> GetCollections(); - Task> GetPagedCollectionData(string collectionName, VectorFilter filter); - Task> GetCollectionData(string collectionName, IEnumerable ids, bool withPayload = false, bool withVector = false); - Task CreateCollection(string collectionName, int dimension); - Task DeleteCollection(string collectionName); - Task Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary? payload = null); - Task> Search(string collectionName, float[] vector, IEnumerable? fields, int limit = 5, float confidence = 0.5f, bool withVector = false); - Task DeleteCollectionData(string collectionName, List ids); - Task DeleteCollectionAllData(string collectionName); + Task DoesCollectionExist(string collectionName) + => throw new NotImplementedException(); + Task> GetCollections() + => throw new NotImplementedException(); + Task> GetPagedCollectionData(string collectionName, VectorFilter filter) + => throw new NotImplementedException(); + Task> GetCollectionData(string collectionName, IEnumerable ids, + bool withPayload = false, bool withVector = false) + => throw new NotImplementedException(); + Task CreateCollection(string collectionName, int dimension) + => throw new NotImplementedException(); + Task DeleteCollection(string collectionName) + => throw new NotImplementedException(); + Task Upsert(string collectionName, Guid id, float[] vector, string text, Dictionary? payload = null) + => throw new NotImplementedException(); + Task> Search(string collectionName, float[] vector, IEnumerable? fields, + int limit = 5, float confidence = 0.5f, bool withVector = false) + => throw new NotImplementedException(); + Task DeleteCollectionData(string collectionName, List ids) + => throw new NotImplementedException(); + Task DeleteCollectionAllData(string collectionName) + => throw new NotImplementedException(); + Task> GetCollectionSnapshots(string collectionName) + => throw new NotImplementedException(); + Task CreateCollectionShapshot(string collectionName) + => throw new NotImplementedException(); + Task DownloadCollectionSnapshot(string collectionName, string snapshotFileName) + => throw new NotImplementedException(); + Task RecoverCollectionFromShapshot(string collectionName, string snapshotFileName, BinaryData snapshotData) + => throw new NotImplementedException(); + Task DeleteCollectionShapshot(string collectionName, string snapshotName) + => throw new NotImplementedException(); } diff --git a/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/Snapshot/VectorCollectionSnapshot.cs b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/Snapshot/VectorCollectionSnapshot.cs new file mode 100644 index 00000000..a8405a80 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/VectorStorage/Models/Snapshot/VectorCollectionSnapshot.cs @@ -0,0 +1,9 @@ +namespace BotSharp.Abstraction.VectorStorage.Models; + +public class VectorCollectionSnapshot +{ + public string Name { get; set; } = default!; + public long Size { get; set; } + public DateTime CreatedTime { get; set; } + public string? CheckSum { get; set; } +} \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs index c95d819d..93cfefde 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/DashboardController.cs @@ -19,16 +19,16 @@ public class DashboardController : ControllerBase } #region User Components [HttpGet("/dashboard/components")] - public async Task GetComponents() + public async Task GetComponents() { var userService = _services.GetRequiredService(); var dashboardProfile = await userService.GetDashboard(); - if (dashboardProfile == null) return new UserDashboardModel(); + if (dashboardProfile == null) return new(); - var result = new UserDashboardModel + var result = new UserDashboardViewModel { ConversationList = dashboardProfile.ConversationList.Select( - x => new UserDashboardConversationModel + x => new UserDashboardConversationViewModel { Name = x.Name, ConversationId = x.ConversationId, @@ -40,7 +40,7 @@ public class DashboardController : ControllerBase } [HttpPost("/dashboard/component/conversation")] - public async Task UpdateDashboardConversationInstruction(UserDashboardConversationModel dashConv) + public async Task UpdateDashboardConversationInstruction(UserDashboardConversationViewModel dashConv) { if (string.IsNullOrEmpty(dashConv.Name) && string.IsNullOrEmpty(dashConv.Instruction)) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs index 50b7c528..f5c67070 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/KnowledgeBaseController.cs @@ -12,7 +12,9 @@ public class KnowledgeBaseController : ControllerBase private readonly IKnowledgeService _knowledgeService; private readonly IServiceProvider _services; - public KnowledgeBaseController(IKnowledgeService knowledgeService, IServiceProvider services) + public KnowledgeBaseController( + IKnowledgeService knowledgeService, + IServiceProvider services) { _knowledgeService = knowledgeService; _services = services; @@ -117,6 +119,46 @@ public class KnowledgeBaseController : ControllerBase #endregion + #region Snapshot + [HttpGet("/knowledge/vector/{collection}/snapshots")] + public async Task> GetVectorCollectionSnapshots([FromRoute] string collection) + { + var snapshots = await _knowledgeService.GetVectorCollectionSnapshots(collection); + return snapshots.Select(x => VectorCollectionSnapshotViewModel.From(x)); + } + + [HttpPost("/knowledge/vector/{collection}/snapshot")] + public async Task CreateVectorCollectionSnapshot([FromRoute] string collection) + { + var snapshot = await _knowledgeService.CreateVectorCollectionSnapshot(collection); + return VectorCollectionSnapshotViewModel.From(snapshot); + } + + [HttpGet("/knowledge/vector/{collection}/snapshot")] + public async Task GetVectorCollectionSnapshot([FromRoute] string collection, [FromQuery] string snapshotFileName) + { + var snapshot = await _knowledgeService.DownloadVectorCollectionSnapshot(collection, snapshotFileName); + return BuildFileResult(snapshotFileName, snapshot); + } + + [HttpPost("/knowledge/vector/{collection}/snapshot/recover")] + public async Task RecoverVectorCollectionFromSnapshot([FromRoute] string collection, IFormFile snapshotFile) + { + var fileName = snapshotFile.FileName; + var binary = FileUtility.BuildBinaryDataFromFile(snapshotFile); + var done = await _knowledgeService.RecoverVectorCollectionFromSnapshot(collection, fileName, binary); + return done; + } + + [HttpDelete("/knowledge/vector/{collection}/snapshot")] + public async Task DeleteVectorCollectionSnapshots([FromRoute] string collection, [FromBody] DeleteVectorCollectionSnapshotRequest request) + { + var done = await _knowledgeService.DeleteVectorCollectionSnapshot(collection, request.SnapshotName); + return done; + } + #endregion + + #region Document [HttpPost("/knowledge/document/{collection}/upload")] public async Task UploadKnowledgeDocuments([FromRoute] string collection, [FromBody] VectorKnowledgeUploadRequest request) @@ -187,7 +229,6 @@ public class KnowledgeBaseController : ControllerBase #endregion - #region Graph [HttpPost("/knowledge/graph/search")] public async Task SearchGraphKnowledge([FromBody] SearchGraphKnowledgeRequest request) @@ -214,4 +255,13 @@ public class KnowledgeBaseController : ControllerBase return saved ? "Success" : "Fail"; } #endregion + + #region Private methods + private FileStreamResult BuildFileResult(string fileName, BinaryData fileData) + { + var stream = fileData.ToStream(); + stream.Position = 0; + return File(stream, "application/octet-stream", Path.GetFileName(fileName)); + } + #endregion } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentCreationModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskCreateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTaskCreateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskCreateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTaskCreateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTaskUpdateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskUpdateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTaskUpdateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTemplatePatchModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTemplatePatchModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentTemplatePatchModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/AgentUpdateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/RoutingRuleUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/RoutingRuleUpdateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/RoutingRuleUpdateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/Request/RoutingRuleUpdateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentTaskViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentTaskViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/View/AgentViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/ConversationCreationModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationCreationModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/ConversationCreationModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/ConversationSummaryModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationSummaryModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/ConversationSummaryModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/InputMessageFiles.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/InputMessageFiles.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/InputMessageFiles.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/InputMessageFiles.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MigrateLatestStateRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/MigrateLatestStateRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/MigrateLatestStateRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/MigrateLatestStateRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/NewMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/NewMessageModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/NewMessageModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/NewMessageModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleAliasModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationTitleAliasModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleAliasModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationTitleAliasModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationTitleModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateConversationTitleModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateMessageModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateMessageModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Request/UpdateMessageModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Response/ChatResponseModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ChatResponseModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/Response/ChatResponseModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/View/ConversationViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/ConversationViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/View/ConversationViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/Request/EmbeddingInputModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/EmbeddingInputModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Embeddings/Request/EmbeddingInputModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/View/MessageFileViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/MessageFileViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Files/View/MessageFileViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructBaseRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructBaseRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructMessageModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructMessageModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructMessageModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/ImageGenerationViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/ImageGenerationViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/ImageGenerationViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/ImageGenerationViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructBaseViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/InstructBaseViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructBaseViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/InstructBaseViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructionLogViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/InstructionLogViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/InstructionLogViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/InstructionLogViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/MultiModalViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/MultiModalViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/MultiModalViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/MultiModalViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/PdfCompletionViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/PdfCompletionViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/PdfCompletionViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/PdfCompletionViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/SpeechToTextViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/SpeechToTextViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/SpeechToTextViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/View/SpeechToTextViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/CreateVectorCollectionRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/CreateVectorCollectionRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/CreateVectorCollectionRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/CreateVectorCollectionRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/DeleteVectorCollectionSnapshotRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/DeleteVectorCollectionSnapshotRequest.cs new file mode 100644 index 00000000..30398b2c --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/DeleteVectorCollectionSnapshotRequest.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Knowledges; + +public class DeleteVectorCollectionSnapshotRequest +{ + [JsonPropertyName("snapshot_name")] + public string SnapshotName { get; set; } = default!; +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/GetKnowledgeDocsRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/GetKnowledgeDocsRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/GetKnowledgeDocsRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/GetKnowledgeDocsRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchGraphKnowledgeRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchGraphKnowledgeRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchGraphKnowledgeRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchVectorKnowledgeRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchVectorKnowledgeRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/SearchVectorKnowledgeRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/SearchVectorKnowledgeRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeCreateRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeCreateRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeCreateRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeCreateRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUpdateRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeUpdateRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUpdateRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeUpdateRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUploadRequest.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeUploadRequest.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeUploadRequest.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/Request/VectorKnowledgeUploadRequest.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/GraphKnowledgeViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/GraphKnowledgeViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/GraphKnowledgeViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/GraphKnowledgeViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeFileViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/KnowledgeFileViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/KnowledgeFileViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/KnowledgeFileViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorCollectionConfigViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionConfigViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorCollectionConfigViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionConfigViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionSnapshotViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionSnapshotViewModel.cs new file mode 100644 index 00000000..95b8a325 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorCollectionSnapshotViewModel.cs @@ -0,0 +1,35 @@ +using BotSharp.Abstraction.VectorStorage.Models; +using System.Text.Json.Serialization; + +namespace BotSharp.OpenAPI.ViewModels.Knowledges; + +public class VectorCollectionSnapshotViewModel +{ + [JsonPropertyName("name")] + public string Name { get; set; } = default!; + + [JsonPropertyName("size")] + public long Size { get; set; } + + [JsonPropertyName("created_time")] + public DateTime CreatedTime { get; set; } + + [JsonPropertyName("check_sum")] + public string? CheckSum { get; set; } + + public static VectorCollectionSnapshotViewModel? From(VectorCollectionSnapshot? model) + { + if (model == null) + { + return null; + } + + return new VectorCollectionSnapshotViewModel + { + Name = model.Name, + Size = model.Size, + CreatedTime = model.CreatedTime, + CheckSum = model.CheckSum + }; + } +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorKnowledgeViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/VectorKnowledgeViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Knowledges/View/VectorKnowledgeViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleAgentActionViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/Request/RoleAgentActionViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleAgentActionViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/Request/RoleAgentActionViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/Request/RoleViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/Request/RoleViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/View/RoleUpdateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/RoleUpdateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Roles/View/RoleUpdateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserAvatarModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserAvatarModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserAvatarModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserAvatarModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserCreationModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserCreationModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserCreationModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserResetPasswordModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserResetPasswordModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserResetPasswordModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserResetPasswordModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserUpdateModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserUpdateModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/Request/UserUpdateModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserAgentActionViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserAgentActionViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserAgentActionViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserAgentActionViewModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserDashboardConversationViewModel.cs similarity index 57% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserDashboardConversationViewModel.cs index 3a5f3491..7165fa87 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserDashboardConversationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserDashboardConversationViewModel.cs @@ -1,19 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Text.Json.Serialization; -using System.Threading.Tasks; namespace BotSharp.OpenAPI.ViewModels.Users; -public class UserDashboardModel -{ +public class UserDashboardViewModel +{ [JsonPropertyName("conversation_list")] - public IList ConversationList { get; set; } = []; + public IList ConversationList { get; set; } = []; } -public class UserDashboardConversationModel +public class UserDashboardConversationViewModel { [JsonPropertyName("name")] public string? Name { get; set; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserViewModel.cs similarity index 100% rename from src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs rename to src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/View/UserViewModel.cs diff --git a/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs b/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs index 0465dc4e..99378ddc 100644 --- a/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs +++ b/src/Plugins/BotSharp.Plugin.Graph/GraphDb.cs @@ -60,9 +60,9 @@ public class GraphDb : IGraphDb using (var client = http.CreateClient()) { - var uri = new Uri(url); try { + var uri = new Uri(url); var data = JsonSerializer.Serialize(request, _jsonOptions); var message = new HttpRequestMessage { diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Snapshot.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Snapshot.cs new file mode 100644 index 00000000..4b839332 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/Services/KnowledgeService.Snapshot.cs @@ -0,0 +1,64 @@ +namespace BotSharp.Plugin.KnowledgeBase.Services; + +public partial class KnowledgeService +{ + public async Task> GetVectorCollectionSnapshots(string collectionName) + { + if (string.IsNullOrWhiteSpace(collectionName)) + { + return Enumerable.Empty(); + } + + var db = GetVectorDb(); + var snapshots = await db.GetCollectionSnapshots(collectionName); + return snapshots; + } + + public async Task CreateVectorCollectionSnapshot(string collectionName) + { + if (string.IsNullOrWhiteSpace(collectionName)) + { + return null; + } + + var db = GetVectorDb(); + var snapshot = await db.CreateCollectionShapshot(collectionName); + return snapshot; + } + + public async Task DownloadVectorCollectionSnapshot(string collectionName, string snapshotFileName) + { + if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(snapshotFileName)) + { + return BinaryData.Empty; + } + + var db = GetVectorDb(); + var snapshot = await db.DownloadCollectionSnapshot(collectionName, snapshotFileName); + return snapshot; + } + + public async Task RecoverVectorCollectionFromSnapshot(string collectionName, string snapshotFileName, BinaryData snapshotData) + { + if (string.IsNullOrWhiteSpace(collectionName)) + { + return false; + } + + var db = GetVectorDb(); + var done = await db.RecoverCollectionFromShapshot(collectionName, snapshotFileName, snapshotData); + return done; + } + + public async Task DeleteVectorCollectionSnapshot(string collectionName, string snapshotName) + { + if (string.IsNullOrWhiteSpace(collectionName) || string.IsNullOrWhiteSpace(snapshotName)) + { + return false; + } + + var db = GetVectorDb(); + var done = await db.DeleteCollectionShapshot(collectionName, snapshotName); + return done; + } +} diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/Models/RecoverFromSnapshotResponse.cs b/src/Plugins/BotSharp.Plugin.Qdrant/Models/RecoverFromSnapshotResponse.cs new file mode 100644 index 00000000..f346aa1b --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Qdrant/Models/RecoverFromSnapshotResponse.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.Qdrant.Models; + +public class RecoverFromSnapshotResponse +{ + [JsonPropertyName("time")] + public decimal Time { get; set; } + + [JsonPropertyName("status")] + public string Status { get; set; } + + [JsonPropertyName("result")] + public bool Result { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs index ef9faa93..3c5d61e5 100644 --- a/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs +++ b/src/Plugins/BotSharp.Plugin.Qdrant/QdrantDb.cs @@ -1,8 +1,14 @@ +using BotSharp.Abstraction.Options; using BotSharp.Abstraction.Utilities; using BotSharp.Abstraction.VectorStorage.Models; +using BotSharp.Plugin.Qdrant.Models; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Qdrant.Client; using Qdrant.Client.Grpc; +using System.Net.Http; +using System.Net.Mime; +using System.Text.Json; namespace BotSharp.Plugin.Qdrant; @@ -10,15 +16,18 @@ public class QdrantDb : IVectorDb { private QdrantClient _client; private readonly QdrantSetting _setting; + private readonly BotSharpOptions _options; private readonly IServiceProvider _services; private readonly ILogger _logger; public QdrantDb( QdrantSetting setting, + BotSharpOptions options, ILogger logger, IServiceProvider services) { _setting = setting; + _options = options; _logger = logger; _services = services; } @@ -39,6 +48,7 @@ public class QdrantDb : IVectorDb return _client; } + #region Collection public async Task DoesCollectionExist(string collectionName) { var client = GetClient(); @@ -86,7 +96,9 @@ public class QdrantDb : IVectorDb var collections = await GetClient().ListCollectionsAsync(); return collections.ToList(); } + #endregion + #region Collection data public async Task> GetPagedCollectionData(string collectionName, VectorFilter filter) { var exist = await DoesCollectionExist(collectionName); @@ -332,4 +344,150 @@ public class QdrantDb : IVectorDb var result = await client.DeleteAsync(collectionName, new Filter()); return result.Status == UpdateStatus.Completed; } + #endregion + + #region Snapshots + public async Task> GetCollectionSnapshots(string collectionName) + { + var exist = await DoesCollectionExist(collectionName); + if (!exist) + { + return Enumerable.Empty(); + } + + var client = GetClient(); + var data = await client.ListSnapshotsAsync(collectionName); + var snapshots = data.Select(x => new VectorCollectionSnapshot + { + Name = x.Name, + Size = x.Size, + CreatedTime = x.CreationTime.ToDateTime(), + CheckSum = x.Checksum + }); + return snapshots; + } + + public async Task CreateCollectionShapshot(string collectionName) + { + var exist = await DoesCollectionExist(collectionName); + if (!exist) + { + return null; + } + + var client = GetClient(); + var desc = await client.CreateSnapshotAsync(collectionName); + if (desc == null) + { + return null; + } + + return new VectorCollectionSnapshot + { + Name = desc.Name, + Size = desc.Size, + CreatedTime = desc.CreationTime.ToDateTime(), + CheckSum = desc.Checksum + }; + } + + public async Task DownloadCollectionSnapshot(string collectionName, string snapshotFileName) + { + var exist = await DoesCollectionExist(collectionName); + if (!exist) + { + return BinaryData.Empty; + } + + var domain = $"https://{_setting.Url}:6333"; + var url = $"{domain}/collections/{collectionName}/snapshots/{snapshotFileName}"; + + var http = _services.GetRequiredService(); + using (var client = http.CreateClient()) + { + try + { + var uri = new Uri(url); + var message = new HttpRequestMessage + { + Method = HttpMethod.Get, + RequestUri = uri + }; + + client.DefaultRequestHeaders.Add("api-key", _setting.ApiKey); + var rawResponse = await client.SendAsync(message); + rawResponse.EnsureSuccessStatusCode(); + + using var contentStream = await rawResponse.Content.ReadAsStreamAsync(); + return BinaryData.FromStream(contentStream); + } + catch (Exception ex) + { + _logger.LogError($"Error when downloading Qdrant snapshot (Endpoint: {url}, Snapshot: {snapshotFileName}). {ex.Message}\r\n{ex.InnerException}"); + return BinaryData.Empty; + } + } + } + + public async Task RecoverCollectionFromShapshot(string collectionName, string snapshotFileName, BinaryData snapshotData) + { + var domain = $"https://{_setting.Url}:6333"; + var url = $"{domain}/collections/{collectionName}/snapshots/upload"; + + var http = _services.GetRequiredService(); + using (var client = http.CreateClient()) + { + try + { + var uri = new Uri(url); + var data = new MultipartFormDataContent + { + { new StringContent(snapshotFileName), "name" }, + { new StringContent(MediaTypeNames.Application.Octet), "type" }, + { new StreamContent(snapshotData.ToStream()), "snapshot", snapshotFileName } + }; + + var message = new HttpRequestMessage + { + Method = HttpMethod.Post, + RequestUri = uri, + Content = data + }; + + client.DefaultRequestHeaders.Add("api-key", _setting.ApiKey); + var rawResponse = await client.SendAsync(message); + rawResponse.EnsureSuccessStatusCode(); + + var responseStr = await rawResponse.Content.ReadAsStringAsync(); + var response = JsonSerializer.Deserialize(responseStr, _options.JsonSerializerOptions); + return response?.Result == true; + } + catch (Exception ex) + { + _logger.LogError($"Error when uploading Qdrant snapshot (Endpoint: {url}, Snapshot: {snapshotFileName}). {ex.Message}\r\n{ex.InnerException}"); + return false; + } + } + } + + public async Task DeleteCollectionShapshot(string collectionName, string snapshotName) + { + var exist = await DoesCollectionExist(collectionName); + if (!exist) + { + return false; + } + + try + { + var client = GetClient(); + await client.DeleteSnapshotAsync(collectionName, snapshotName); + return true; + } + catch + { + return false; + } + } + #endregion } From 14c476ce0da38e7419dcc24ce2b4453d7c1881e7 Mon Sep 17 00:00:00 2001 From: Haiping Chen Date: Wed, 12 Mar 2025 18:30:53 -0500 Subject: [PATCH 10/11] OnUserDisconnected --- .../BotSharp.Abstraction/Conversations/ConversationHookBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs index 0f115d5d..a84c8693 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs @@ -79,6 +79,6 @@ public abstract class ConversationHookBase : IConversationHook public virtual Task OnNotificationGenerated(RoleDialogModel message) => Task.CompletedTask; - public Task OnUserDisconnected(Conversation conversation) + public virtual Task OnUserDisconnected(Conversation conversation) => Task.CompletedTask; } From d2ba1a3dc0f23b467e4a4e5d2b6ca2a5634a66a5 Mon Sep 17 00:00:00 2001 From: Visagan Guruparan <103048@smsassist.com> Date: Wed, 12 Mar 2025 21:35:30 -0500 Subject: [PATCH 11/11] Update default wait time for web driver --- .../Browsing/Settings/WebBrowsingSettings.cs | 2 ++ .../UtilFunctions/UtilWebGoToPageFn.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index 7521a3e4..f876c449 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -7,4 +7,6 @@ public class WebBrowsingSettings // Default timeout in milliseconds public float DefaultTimeout { get; set; } = 30000; public bool IsEnableScreenshot { get; set; } + // Default wait time in seconds after page is opened + public int DefaultWaitTime { get; set; } = 5; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs index a29af212..0c5507bf 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebGoToPageFn.cs @@ -26,7 +26,7 @@ public class UtilWebGoToPageFn : IFunctionCallback var _webDriver = _services.GetRequiredService(); args.Timeout = _webDriver.DefaultTimeout; args.WaitForNetworkIdle = false; - args.WaitTime = 5; + args.WaitTime = _webDriver.DefaultWaitTime; args.OpenNewTab = true; var conv = _services.GetRequiredService();