diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs index e1ae9a79..f876c449 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Settings/WebBrowsingSettings.cs @@ -4,4 +4,9 @@ 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 bool IsEnableScreenshot { get; set; } + // Default wait time in seconds after page is opened + public int DefaultWaitTime { get; set; } = 5; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs b/src/Infrastructure/BotSharp.Abstraction/Conversations/ConversationHookBase.cs index da87646e..a84c8693 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 virtual 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/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/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/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.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.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.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/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/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/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/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.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.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.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 diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs index 0d0947e5..f6a6322c 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs @@ -72,4 +72,11 @@ public class InputAudioTranscription { [JsonPropertyName("model")] public string Model { get; set; } = null!; + + [JsonPropertyName("language")] + public string Language { get; set; } = "en"; + + [JsonPropertyName("prompt")] + [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 0d6f76b4..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]; @@ -138,7 +140,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)) @@ -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", @@ -319,6 +327,8 @@ public class RealTimeCompletionProvider : IRealTimeCompletion InputAudioTranscription = new InputAudioTranscription { Model = "whisper-1", + Language = "en", + Prompt = string.Join(", ", words.Select(x => x.ToLower().Trim()).Distinct()).SubstringMax(1024) }, Voice = "alloy", Instructions = instruction, @@ -329,7 +339,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion MaxResponseOutputTokens = 512, TurnDetection = new RealtimeSessionTurnDetection { - Threshold = 0.8f, + Threshold = 0.9f, PrefixPadding = 300, SilenceDuration = 800 } @@ -662,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.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 } diff --git a/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs b/src/Plugins/BotSharp.Plugin.Twilio/OutboundPhoneCallHandler/Functions/HangupPhoneCallFn.cs index f54c1b42..cd755306 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 been 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 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; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs index e1041ae6..fe69f9d1 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..0beb80fc 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..0c5507bf 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,8 +23,10 @@ public class UtilWebGoToPageFn : IFunctionCallback { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }); + 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();