diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs index f071caaf..50d1be93 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentHook.cs @@ -15,7 +15,7 @@ public interface IAgentHook bool OnInstructionLoaded(string template, Dictionary dict); - bool OnFunctionsLoaded(ref string functions); + bool OnFunctionsLoaded(ref List functions); bool OnSamplesLoaded(ref string samples); diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs index d1086c26..269a6983 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentRouting.cs @@ -6,6 +6,6 @@ public interface IAgentRouting { string AgentId { get; } Task LoadRouter(); - RoutingRecord[] GetRoutingRecords(); - RoutingRecord GetRecordByName(string name); + RoutingItem[] GetRoutingRecords(); + RoutingItem GetRecordByName(string name); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs index 1297fca4..2f1f1e6e 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/Agent.cs @@ -21,18 +21,36 @@ public class Agent /// /// Functions /// - public string Functions { get; set; } + public List Functions { get; set; } + + /// + /// Responses + /// + public List Responses { get; set; } /// /// Domain knowledges /// public string Knowledges { get; set; } - /// - /// Routes - /// - public List Routes { get; set; } - public override string ToString() => $"{Name} {Id}"; + + public Agent SetInstruction(string instruction) + { + Instruction = instruction; + return this; + } + + public Agent SetFunctions(List functions) + { + Functions = functions; + return this; + } + + public Agent SetResponses(List responses) + { + Responses = responses; + return this; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 8c3dc959..3265d2f0 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -11,4 +11,8 @@ public interface IBotSharpRepository IQueryable Conversation { get; } int Transaction(Action action); void Add(object entity); + + UserRecord GetUserByEmail(string email); + void CreateUser(UserRecord user); + void UpdateAgent(AgentRecord agent); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/AgentRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/AgentRecord.cs index c3300440..8c3b0268 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/AgentRecord.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/AgentRecord.cs @@ -11,9 +11,9 @@ public class AgentRecord : RecordBase public string Instruction { get; set; } - public string Functions { get; set; } + public List Functions { get; set; } - public List Routes { get; set; } + public List Responses { get; set; } [Required] public DateTime CreatedTime { get; set; } @@ -30,7 +30,6 @@ public class AgentRecord : RecordBase Description = agent.Description, Instruction = agent.Instruction, Functions = agent.Functions, - Routes = agent.Routes, }; } @@ -43,9 +42,33 @@ public class AgentRecord : RecordBase Description = Description, Instruction = Instruction, Functions = Functions, - Routes = Routes, CreatedDateTime = CreatedTime, UpdatedDateTime = UpdatedTime }; } + + public AgentRecord SetId(string id) + { + Id = id; + return this; + } + + + public AgentRecord SetInstruction(string instruction) + { + Instruction = instruction; + return this; + } + + public AgentRecord SetFunctions(List functions) + { + Functions = functions; + return this; + } + + public AgentRecord SetResponses(List responses) + { + Responses = responses; + return this; + } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingItemRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingItemRecord.cs new file mode 100644 index 00000000..b549723c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingItemRecord.cs @@ -0,0 +1,11 @@ +namespace BotSharp.Abstraction.Repositories.Records; + +public class RoutingItemRecord : RecordBase +{ + public string AgentId { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public List RequiredFields { get; set; } = new List(); + public string RedirectTo { get; set; } + public bool Disabled { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingProfileRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingProfileRecord.cs new file mode 100644 index 00000000..d54c4c40 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/RoutingProfileRecord.cs @@ -0,0 +1,7 @@ +namespace BotSharp.Abstraction.Repositories.Records; + +public class RoutingProfileRecord : RecordBase +{ + public string Name { get; set; } + public List AgentIds { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs index 6500ba27..bbefdc47 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Records/UserRecord.cs @@ -25,7 +25,7 @@ public class UserRecord : RecordBase public string Password { get; set; } = string.Empty; [MaxLength(36)] - public string? ExternalId { get; set; } + public string ExternalId { get; set; } [Required] public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs similarity index 96% rename from src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs rename to src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs index 73fd4d1e..3295e70b 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingRecord.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingItem.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; namespace BotSharp.Abstraction.Routing.Models; -public class RoutingRecord +public class RoutingItem { [JsonPropertyName("agent_id")] public string AgentId { get; set; } diff --git a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfile.cs similarity index 87% rename from src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs rename to src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfile.cs index 753881ec..d92d4d82 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfileRecord.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Routing/Models/RoutingProfile.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; namespace BotSharp.Abstraction.Routing.Models; -public class RoutingProfileRecord +public class RoutingProfile { [JsonPropertyName("name")] public string Name { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentHookBase.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentHookBase.cs index eaf28792..a5fe8649 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentHookBase.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentHookBase.cs @@ -31,7 +31,7 @@ public abstract class AgentHookBase : IAgentHook return true; } - public virtual bool OnFunctionsLoaded(ref string functions) + public virtual bool OnFunctionsLoaded(ref List functions) { _agent.Functions = functions; return true; diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs index ef7443ae..a5762b66 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs @@ -1,7 +1,13 @@ using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Agents.Settings; +using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Records; +using BotSharp.Abstraction.Users.Models; using MongoDB.Bson; +using System.IO; +using Tensorflow; +using static Tensorflow.TensorShapeProto.Types; namespace BotSharp.Core.Agents.Services; @@ -14,7 +20,7 @@ public partial class AgentService var record = (from a in db.Agent join ua in db.UserAgent on a.Id equals ua.AgentId join u in db.User on ua.UserId equals u.Id - where (ua.UserId == _user.Id || u.ExternalId == _user.Id) && a.Name == agent.Name + where u.ExternalId == _user.Id && a.Name == agent.Name select a).FirstOrDefault(); if (record != null) @@ -23,15 +29,29 @@ public partial class AgentService } record = AgentRecord.FromAgent(agent); - record.Id = ObjectId.GenerateNewId().ToString(); + record.Id = Guid.NewGuid().ToString(); record.CreatedTime = DateTime.UtcNow; record.UpdatedTime = DateTime.UtcNow; + var dbSettings = _services.GetRequiredService(); + var agentSettings = _services.GetRequiredService(); + var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir); + var foundAgent = FetchAgentInfoFromFile(agent.Name, filePath); + + if (foundAgent != null) + { + record.SetId(foundAgent.Id) + .SetInstruction(foundAgent.Instruction) + .SetFunctions(foundAgent.Functions) + .SetResponses(foundAgent.Responses); + } + var user = db.User.FirstOrDefault(x => x.ExternalId == _user.Id); var userAgentRecord = new UserAgentRecord { - UserId = user?.Id ?? ObjectId.GenerateNewId().ToString(), - AgentId = record.Id, + Id = Guid.NewGuid().ToString(), + UserId = user.Id, + AgentId = foundAgent?.Id ?? record.Id, CreatedTime = DateTime.UtcNow, UpdatedTime = DateTime.UtcNow }; @@ -44,4 +64,62 @@ public partial class AgentService return record.ToAgent(); } + + private JsonSerializerOptions _options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + private Agent FetchAgentInfoFromFile(string agentName, string filePath) + { + foreach (var dir in Directory.GetDirectories(filePath)) + { + var agentJson = File.ReadAllText(Path.Combine(dir, "agent.json")); + var agent = JsonSerializer.Deserialize(agentJson, _options); + if (agent != null && agent.Name == agentName) + { + var functions = FetchFunctionsFromFile(dir); + var instruction = FetchInstructionFromFile(dir); + var responses = FetchResponsesFromFile(dir); + return agent.SetInstruction(instruction).SetFunctions(functions).SetResponses(responses); + } + } + + return null; + } + + private string FetchInstructionFromFile(string fileDir) + { + var file = Path.Combine(fileDir, "instruction.liquid"); + if (!File.Exists(file)) return null; + + var instruction = File.ReadAllText(file); + return instruction; + } + + private List FetchFunctionsFromFile(string fileDir) + { + var file = Path.Combine(fileDir, "functions.json"); + if (!File.Exists(file)) return new List(); + + var functionsJson = File.ReadAllText(file); + var functionDefs = JsonSerializer.Deserialize>(functionsJson, _options); + var functions = functionDefs.Select(x => JsonSerializer.Serialize(x, _options)).ToList(); + return functions; + } + + private List FetchResponsesFromFile(string fileDir) + { + var responses = new List(); + var responseDir = Path.Combine(fileDir, "responses"); + if (!Directory.Exists(responseDir)) return responses; + + foreach (var file in Directory.GetFiles(responseDir)) + { + responses.Add(File.ReadAllText(file)); + } + return responses; + } } diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs index e2f13567..9243d28a 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.LoadAgent.cs @@ -30,7 +30,7 @@ public partial class AgentService hook.OnInstructionLoaded(agent.Instruction, templateDict); } - if (!string.IsNullOrEmpty(agent.Functions)) + if (agent.Functions != null && agent.Functions.Any()) { var functions = agent.Functions; hook.OnFunctionsLoaded(ref functions); diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs index cf1f32e8..a14620f1 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.UpdateAgent.cs @@ -10,41 +10,30 @@ public partial class AgentService { var db = _services.GetRequiredService(); - db.Transaction(delegate - { - var record = (from a in db.Agent - join ua in db.UserAgent on a.Id equals ua.AgentId - join u in db.User on ua.UserId equals u.Id - where (ua.UserId == _user.Id || u.ExternalId == _user.Id) && - a.Id == agent.Id - select a).FirstOrDefault(); + var record = (from a in db.Agent + join ua in db.UserAgent on a.Id equals ua.AgentId + join u in db.User on ua.UserId equals u.Id + where (ua.UserId == _user.Id || u.ExternalId == _user.Id) && + a.Id == agent.Id + select a).FirstOrDefault(); - if (record == null) return; + if (record == null) return; - record.Name = agent.Name; + record.Name = agent.Name; - if (!string.IsNullOrEmpty(agent.Description)) - record.Description = agent.Description; + if (!string.IsNullOrEmpty(agent.Description)) + record.Description = agent.Description; - if (!string.IsNullOrEmpty(agent.Instruction)) - record.Instruction = agent.Instruction; + if (!string.IsNullOrEmpty(agent.Instruction)) + record.Instruction = agent.Instruction; - if (!string.IsNullOrEmpty(agent.Functions)) - record.Functions = agent.Functions; + if (agent.Functions != null && agent.Functions.Any()) + record.Functions = agent.Functions; - if (!agent.Routes.IsEmpty()) - record.Routes = agent.Routes; + if (agent.Responses != null && agent.Responses.Any()) + record.Responses = agent.Responses; - record.UpdatedTime = DateTime.UtcNow; - db.Add(record); - }); - - // Save instruction to file - //var dir = GetAgentDataDir(agent.Id); - //var instructionFile = Path.Combine(dir, "instruction.txt"); - //File.WriteAllText(instructionFile, agent.Instruction); - - //var samplesFile = Path.Combine(dir, "samples.txt"); - //File.WriteAllText(samplesFile, agent.Samples); + db.UpdateAgent(record); + await Task.CompletedTask; } } diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs index b5073133..ccaf2662 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs @@ -92,25 +92,27 @@ public class ConversationStateService : IConversationStateService, IDisposable public void Save() { - var states = new StringBuilder(); - var conversation = _db.Conversation.FirstOrDefault(x => x.Id == _conversationId); + //var states = new StringBuilder(); + //var conversation = _db.Conversation.FirstOrDefault(x => x.Id == _conversationId); + + var states = new List(); foreach (var dic in _state) { - //states.Add($"{dic.Key}={dic.Value}"); - states.AppendLine($"{dic.Key}={dic.Value}"); + states.Add($"{dic.Key}={dic.Value}"); + //states.AppendLine($"{dic.Key}={dic.Value}"); } - //File.WriteAllLines(_file, states); + File.WriteAllLines(_file, states); _logger.LogInformation($"Saved state {_conversationId}"); - if (conversation != null) - { - conversation.State = states.ToString(); - _db.Transaction(delegate - { - _db.Add(conversation); - }); - } + //if (conversation != null) + //{ + // conversation.State = states.ToString(); + // _db.Transaction(delegate + // { + // _db.Add(conversation); + // }); + //} } public void CleanState() @@ -134,7 +136,13 @@ public class ConversationStateService : IConversationStateService, IDisposable { Directory.CreateDirectory(dir); } - return Path.Combine(dir, "state.dict"); + + var stateFile = Path.Combine(dir, "state.dict"); + if (!File.Exists(stateFile)) + { + File.WriteAllText(stateFile, ""); + } + return stateFile; } private string GetConversationState(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs index de56534b..c55c3265 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs @@ -29,10 +29,13 @@ public class ConversationStorage : IConversationStorage public void Append(string conversationId, string agentId, RoleDialogModel dialog) { - var dialogs = GetConversationDialogs(conversationId); - var sb = new StringBuilder(dialogs); + //var dialogs = GetConversationDialogs(conversationId); + //var sb = new StringBuilder(dialogs); var db = _services.GetRequiredService(); + var conversationFile = GetStorageFile(conversationId); + var sb = new StringBuilder(); + if (dialog.Role == AgentRole.Function) { var args = dialog.FunctionArgs.Replace("\r", " ").Replace("\n", " ").Trim(); @@ -60,15 +63,15 @@ public class ConversationStorage : IConversationStorage } var updatedDialogs = sb.ToString(); - //File.AppendAllText(conversationFile, conversation); + File.AppendAllText(conversationFile, updatedDialogs); - var conversation = db.Conversation.FirstOrDefault(x => x.Id == conversationId); - conversation.AgentId = agentId; - conversation.Dialog = updatedDialogs; - db.Transaction(delegate - { - db.Add(conversation); - }); + //var conversation = db.Conversation.FirstOrDefault(x => x.Id == conversationId); + //conversation.AgentId = agentId; + //conversation.Dialog = updatedDialogs; + //db.Transaction(delegate + //{ + // db.Add(conversation); + //}); } public List GetDialogs(string conversationId) diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 34b1b6e6..7fd896dc 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -11,6 +11,21 @@ public class BotSharpDbContext : Database, IBotSharpRepository public IQueryable UserAgent => Table(); public IQueryable Conversation => Table(); + public void UpdateAgent(AgentRecord agent) + { + throw new NotImplementedException(); + } + + public void CreateUser(UserRecord user) + { + throw new NotImplementedException(); + } + + public UserRecord GetUserByEmail(string email) + { + throw new NotImplementedException(); + } + public int Transaction(Action action) { DatabaseFacade database = base.GetMaster(typeof(TTableInterface)).Database; diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs index 57d3f8e6..562a8b0a 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository.cs @@ -1,8 +1,8 @@ +using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Records; using System.IO; using System.Text.Json; - namespace BotSharp.Core.Repository; public class FileRepository : IBotSharpRepository @@ -220,4 +220,60 @@ public class FileRepository : IBotSharpRepository return _changedTableNames.Count; } + + public UserRecord GetUserByEmail(string email) + { + return User.FirstOrDefault(x => x.Email == email); + } + + public void CreateUser(UserRecord user) + { + var userId = Guid.NewGuid().ToString(); + var dir = Path.Combine(_dbSettings.FileRepository, "users", userId); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + var path = Path.Combine(dir, "user.json"); + File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); + } + + public void UpdateAgent(AgentRecord agent) + { + if (agent == null) return; + + var dir = GetAgentDataDir(agent.Id); + + if (!string.IsNullOrEmpty(agent.Instruction)) + { + var instructionFile = Path.Combine(dir, "instruction.liquid"); + File.WriteAllText(instructionFile, agent.Instruction); + } + + if (agent.Functions != null || agent.Functions.Any()) + { + var functionFile = Path.Combine(dir, "functions.json"); + var functions = new List(); + foreach (var function in agent.Functions) + { + var functionDef = JsonSerializer.Deserialize(function, _options); + functions.Add(JsonSerializer.Serialize(functionDef, _options)); + } + + var functionText = JsonSerializer.Serialize(functions, _options); + File.WriteAllText(functionFile, functionText); + } + } + + private string GetAgentDataDir(string agentId) + { + var dbSettings = _services.GetRequiredService(); + var agentSettings = _services.GetRequiredService(); + var dir = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, agentId); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + return dir; + } } diff --git a/src/Infrastructure/BotSharp.Core/Routing/Router.cs b/src/Infrastructure/BotSharp.Core/Routing/Router.cs index 1d3126d0..501d56d1 100644 --- a/src/Infrastructure/BotSharp.Core/Routing/Router.cs +++ b/src/Infrastructure/BotSharp.Core/Routing/Router.cs @@ -29,12 +29,12 @@ public class Router : IAgentRouting return await agentService.LoadAgent(AgentId); } - public RoutingRecord[] GetRoutingRecords() + public RoutingItem[] GetRoutingRecords() { var agentSettings = _services.GetRequiredService(); var dbSettings = _services.GetRequiredService(); - var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, _settings.RouterId, "route.json"); - var records = JsonSerializer.Deserialize(File.ReadAllText(filePath)); + var filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, "route.json"); + var records = JsonSerializer.Deserialize(File.ReadAllText(filePath)); // check if routing profile is specified filePath = Path.Combine(dbSettings.FileRepository, agentSettings.DataDir, "routing-profile.json"); @@ -42,7 +42,7 @@ public class Router : IAgentRouting { var state = _services.GetRequiredService(); var name = state.GetState("channel"); - var profiles = JsonSerializer.Deserialize(File.ReadAllText(filePath)); + var profiles = JsonSerializer.Deserialize(File.ReadAllText(filePath)); var spcificedProfile = profiles.FirstOrDefault(x => x.Name == name); if (spcificedProfile != null) { @@ -53,7 +53,7 @@ public class Router : IAgentRouting return records; } - public RoutingRecord GetRecordByName(string name) + public RoutingItem GetRecordByName(string name) { return GetRoutingRecords().First(x => x.Name.ToLower() == name.ToLower()); } diff --git a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs index d268db4c..240783ff 100644 --- a/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs +++ b/src/Infrastructure/BotSharp.Core/Templating/TemplateRender.cs @@ -17,7 +17,7 @@ public class TemplateRender : ITemplateRender _logger = logger; _options = new TemplateOptions(); _options.MemberAccessStrategy.MemberNameStrategy = MemberNameStrategies.SnakeCase; - _options.MemberAccessStrategy.Register(); + _options.MemberAccessStrategy.Register(); } public string Render(string template, Dictionary dict) diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index c0358b9e..dee8bf33 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -22,7 +22,7 @@ public class UserService : IUserService public async Task CreateUser(User user) { var db = _services.GetRequiredService(); - var record = db.User.FirstOrDefault(x => x.Email == user.Email.ToLower()); + var record = db.GetUserByEmail(user.Email); if (record != null) { return record.ToUser(); @@ -34,11 +34,7 @@ public class UserService : IUserService record.Password = Utilities.HashText(user.Password, record.Salt); record.ExternalId = _user.Id; - db.Transaction(delegate - { - db.Add(record); - }); - + db.CreateUser(record); return record.ToUser(); } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs index 1203c4db..0053adfd 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentCreationModel.cs @@ -7,8 +7,7 @@ public class AgentCreationModel public string Name { get; set; } public string Description { get; set; } public string Instruction { get; set; } - public string Functions { get; set; } - public List Routes { get; set; } + public List Functions { get; set; } public Agent ToAgent() { @@ -18,7 +17,6 @@ public class AgentCreationModel Description = Description, Instruction = Instruction, Functions = Functions, - Routes = Routes }; } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs index 263875e7..3df3fc6a 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentUpdateModel.cs @@ -20,12 +20,12 @@ public class AgentUpdateModel /// /// Functions /// - public string? Functions { get; set; } + public List Functions { get; set; } /// /// Routes /// - public List? Routes { get; set; } + public List Responses { get; set; } public Agent ToAgent() { @@ -43,11 +43,11 @@ public class AgentUpdateModel if (Samples != null) agent.Samples = Samples; - if (Functions != null) + if (Functions != null && Functions.Any()) agent.Functions = Functions; - if (!Routes.IsEmpty()) - agent.Routes = Routes; + if (Responses != null && Responses.Any()) + agent.Responses = Responses; return agent; } diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs index ed84c879..1b68684c 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentViewModel.cs @@ -8,8 +8,8 @@ public class AgentViewModel public string Name { get; set; } public string Description { get; set; } public string Instruction { get; set; } - public string Functions { get; set; } - public List Routes { get; set; } + public List Functions { get; set; } + public List Responses { get; set; } public DateTime UpdatedDateTime { get; set; } public static AgentViewModel FromAgent(Agent agent) @@ -21,7 +21,7 @@ public class AgentViewModel Description = agent.Description, Instruction = agent.Instruction, Functions = agent.Functions, - Routes = agent.Routes, + Responses = agent.Responses, UpdatedDateTime = agent.UpdatedDateTime }; } diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs index 0e8438d8..65ad123b 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/ChatCompletionProvider.cs @@ -69,17 +69,13 @@ public class ChatCompletionProvider : IChatCompletion return samples; } - public List GetFunctions(string functionsJson) + public List GetFunctions(List functionsJson) { - var functions = new List(); - if (!string.IsNullOrEmpty(functionsJson)) + var functions = functionsJson?.Select(x => JsonSerializer.Deserialize(x, new JsonSerializerOptions { - functions = JsonSerializer.Deserialize>(functionsJson, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - AllowTrailingCommas = true - }); - } + PropertyNameCaseInsensitive = true, + AllowTrailingCommas = true + }))?.ToList() ?? new List(); return functions; } diff --git a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/GPT4CompletionProvider.cs b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/GPT4CompletionProvider.cs index 8767e7df..94db6ab8 100644 --- a/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/GPT4CompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/GPT4CompletionProvider.cs @@ -69,17 +69,13 @@ public class GPT4CompletionProvider : IChatCompletion return samples; } - public List GetFunctions(string functionsJson) + public List GetFunctions(List functionsJson) { - var functions = new List(); - if (!string.IsNullOrEmpty(functionsJson)) + var functions = functionsJson?.Select(x => JsonSerializer.Deserialize(x, new JsonSerializerOptions { - functions = JsonSerializer.Deserialize>(functionsJson, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true, - AllowTrailingCommas = true - }); - } + PropertyNameCaseInsensitive = true, + AllowTrailingCommas = true + }))?.ToList() ?? new List(); return functions; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCollection.cs index 339cf1d1..617cad9c 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCollection.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentCollection.cs @@ -4,9 +4,9 @@ public class AgentCollection : MongoBase { public string Name { get; set; } public string Description { get; set; } - public string Functions { get; set; } public string Instruction { get; set; } - public List Routes { get; set; } + public List Functions { get; set; } + public List Responses { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs index 216e14b7..9e1fe51d 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/ConversationCollection.cs @@ -2,8 +2,8 @@ namespace BotSharp.Plugin.MongoStorage.Collections; public class ConversationCollection : MongoBase { - public string AgentId { get; set; } - public string UserId { get; set; } + public Guid AgentId { get; set; } + public Guid UserId { get; set; } public string Title { get; set; } public string Dialog { get; set; } public string State { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserAgentCollection.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserAgentCollection.cs index 3e9f4043..562cea13 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserAgentCollection.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserAgentCollection.cs @@ -2,8 +2,8 @@ namespace BotSharp.Plugin.MongoStorage.Collections; public class UserAgentCollection : MongoBase { - public string UserId { get; set; } - public string AgentId { get; set; } + public Guid UserId { get; set; } + public Guid AgentId { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs index 6fcd9348..7a0c1ef0 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoBase.cs @@ -6,6 +6,6 @@ namespace BotSharp.Plugin.MongoStorage; [BsonIgnoreExtraElements(Inherited = true)] public class MongoBase { - [BsonId(IdGenerator = typeof(StringObjectIdGenerator))] - public string Id { get; set; } + [BsonId(IdGenerator = typeof(GuidGenerator))] + public Guid Id { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs index 12f109b6..af6e5d06 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs @@ -6,12 +6,12 @@ public class MongoStoragePlugin : IBotSharpPlugin { public void RegisterDI(IServiceCollection services, IConfiguration config) { - //services.AddScoped((IServiceProvider x) => - //{ - // var dbSettings = x.GetRequiredService(); - // return new MongoDbContext(dbSettings.MongoDb); - //}); + services.AddScoped((IServiceProvider x) => + { + var dbSettings = x.GetRequiredService(); + return new MongoDbContext(dbSettings.MongoDb); + }); - //services.AddScoped(); + services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs index af7c5617..07a2059e 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.cs @@ -32,12 +32,12 @@ public class MongoRepository : IBotSharpRepository var agentDocs = _dc.Agents?.AsQueryable()?.ToList() ?? new List(); _agents = agentDocs.Select(x => new AgentRecord { - Id = x.Id?.ToString(), + Id = x.Id.ToString(), Name = x.Name, Description = x.Description, Instruction = x.Instruction, Functions = x.Functions, - Routes = x.Routes, + Responses = x.Responses, CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -59,7 +59,7 @@ public class MongoRepository : IBotSharpRepository var userDocs = _dc.Users?.AsQueryable()?.ToList() ?? new List(); _users = userDocs.Select(x => new UserRecord { - Id = x.Id?.ToString(), + Id = x.Id.ToString(), FirstName = x.FirstName, LastName = x.LastName, Email = x.Email, @@ -87,9 +87,9 @@ public class MongoRepository : IBotSharpRepository var userDocs = _dc.UserAgents?.AsQueryable()?.ToList() ?? new List(); _userAgents = userDocs.Select(x => new UserAgentRecord { - Id = x.Id?.ToString(), - AgentId = x.AgentId, - UserId = x.UserId, + Id = x.Id.ToString(), + AgentId = x.AgentId.ToString(), + UserId = x.UserId.ToString(), CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -111,9 +111,9 @@ public class MongoRepository : IBotSharpRepository var conversationDocs = _dc.Conversations?.AsQueryable()?.ToList() ?? new List(); _conversations = conversationDocs.Select(x => new ConversationRecord { - Id = x.Id?.ToString(), - AgentId = x.AgentId, - UserId = x.UserId, + Id = x.Id.ToString(), + AgentId = x.AgentId.ToString(), + UserId = x.UserId.ToString(), Title = x.Title, Dialog = x.Dialog, State = x.State, @@ -161,9 +161,9 @@ public class MongoRepository : IBotSharpRepository { var conversations = _conversations.Select(x => new ConversationCollection { - Id = x.Id.IfNullOrEmptyAs(ObjectId.GenerateNewId().ToString()), - AgentId = x.AgentId, - UserId = x.UserId, + Id = string.IsNullOrEmpty(x.Id) ? Guid.NewGuid() : new Guid(x.Id), + AgentId = Guid.Parse(x.AgentId), + UserId = Guid.Parse(x.UserId), Title = x.Title, Dialog = x.Dialog, State = x.State, @@ -189,12 +189,12 @@ public class MongoRepository : IBotSharpRepository { var agents = _agents.Select(x => new AgentCollection { - Id = x.Id.IfNullOrEmptyAs(ObjectId.GenerateNewId().ToString()), + Id = string.IsNullOrEmpty(x.Id) ? Guid.NewGuid() : new Guid(x.Id), Name = x.Name, Description = x.Description, Instruction = x.Instruction, Functions = x.Functions, - Routes = x.Routes, + Responses = x.Responses, CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -207,7 +207,7 @@ public class MongoRepository : IBotSharpRepository .Set(x => x.Description, agent.Description) .Set(x => x.Instruction, agent.Instruction) .Set(x => x.Functions, agent.Functions) - .Set(x => x.Routes, agent.Routes) + .Set(x => x.Responses, agent.Responses) .Set(x => x.CreatedTime, agent.CreatedTime) .Set(x => x.UpdatedTime, agent.UpdatedTime); _dc.Agents.UpdateOne(filter, update, _options); @@ -217,7 +217,7 @@ public class MongoRepository : IBotSharpRepository { var users = _users.Select(x => new UserCollection { - Id = x.Id.IfNullOrEmptyAs(ObjectId.GenerateNewId().ToString()), + Id = string.IsNullOrEmpty(x.Id) ? Guid.NewGuid() : new Guid(x.Id), FirstName = x.FirstName, LastName = x.LastName, Salt = x.Salt, @@ -247,9 +247,9 @@ public class MongoRepository : IBotSharpRepository { var userAgents = _userAgents.Select(x => new UserAgentCollection { - Id = x.Id.IfNullOrEmptyAs(ObjectId.GenerateNewId().ToString()), - AgentId = x.AgentId, - UserId = x.UserId, + Id = string.IsNullOrEmpty(x.Id) ? Guid.NewGuid() : new Guid(x.Id), + AgentId = Guid.Parse(x.AgentId), + UserId = Guid.Parse(x.UserId), CreatedTime = x.CreatedTime, UpdatedTime = x.UpdatedTime }).ToList(); @@ -269,4 +269,69 @@ public class MongoRepository : IBotSharpRepository return _changedTableNames.Count; } + + public UserRecord GetUserByEmail(string email) + { + var user = User.FirstOrDefault(x => x.Email == email); + return user != null ? new UserRecord + { + Id = user.Id.ToString(), + FirstName = user.FirstName, + LastName = user.LastName, + Email = user.Email, + Password = user.Password, + Salt = user.Salt, + ExternalId = user.ExternalId, + CreatedTime = user.CreatedTime, + UpdatedTime = user.UpdatedTime + } : null; + } + + public void CreateUser(UserRecord user) + { + if (user == null) return; + + var userCollection = new UserCollection + { + Id = Guid.NewGuid(), + FirstName = user.FirstName, + LastName = user.LastName, + Salt = user.Salt, + Password = user.Password, + Email = user.Email, + ExternalId = user.ExternalId, + CreatedTime = DateTime.UtcNow, + UpdatedTime = DateTime.UtcNow + }; + + _dc.Users.InsertOne(userCollection); + } + + public void UpdateAgent(AgentRecord agent) + { + if (agent == null || string.IsNullOrEmpty(agent.Id)) return; + + var agentCollection = new AgentCollection + { + Id = Guid.Parse(agent.Id), + Name = agent.Name, + Description = agent.Description, + Instruction = agent.Instruction, + Functions = agent.Functions, + Responses = agent.Responses, + UpdatedTime = DateTime.UtcNow + }; + + + var filter = Builders.Filter.Eq(x => x.Id, Guid.Parse(agent.Id)); + var update = Builders.Update + .Set(x => x.Name, agent.Name) + .Set(x => x.Description, agent.Description) + .Set(x => x.Instruction, agent.Instruction) + .Set(x => x.Functions, agent.Functions) + .Set(x => x.Responses, agent.Responses) + .Set(x => x.UpdatedTime, agent.UpdatedTime); + + _dc.Agents.UpdateOne(filter, update, _options); + } }