diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index 5ca241e6..bf69bec3 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -18,6 +18,7 @@ public interface IBotSharpRepository #region User User? GetUserByEmail(string email); User? GetUserById(string id); + User? GetUserByUserName(string userName); void CreateUser(User user); #endregion diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs new file mode 100644 index 00000000..267a997c --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IAuthenticationHook.cs @@ -0,0 +1,10 @@ +using BotSharp.Abstraction.Users.Models; +using System.Security.Claims; + +namespace BotSharp.Abstraction.Users; + +public interface IAuthenticationHook +{ + Task Authenticate(string id, string password); + void AddClaims(List claims); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs index 2f7a56e9..a0998117 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/IUserIdentity.cs @@ -7,4 +7,5 @@ public interface IUserIdentity string UserName { get; } string FirstName { get; } string LastName { get; } + string FullName { get; } } diff --git a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs index 2e96d782..7ca441fe 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Users/Models/User.cs @@ -11,6 +11,7 @@ public class User public string Email { get; set; } = string.Empty; public string Salt { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; + public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } = UserRole.Client; public DateTime UpdatedTime { get; set; } = DateTime.UtcNow; diff --git a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs index 9eec21d5..da8691e8 100644 --- a/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Agents/AgentPlugin.cs @@ -36,9 +36,9 @@ public class AgentPlugin : IBotSharpPlugin { SubMenu = new List { - new PluginMenuDef("Router", link: "/page/agent/router"), // icon: "bx bx-map-pin" - new PluginMenuDef("Evaluator", link: "/page/agent/evaluator"), // icon: "bx bx-task" - new PluginMenuDef("Agents", link: "/page/agent"), // icon: "bx bx-bot" + new PluginMenuDef("Router", link: "page/agent/router"), // icon: "bx bx-map-pin" + new PluginMenuDef("Evaluator", link: "page/agent/evaluator"), // icon: "bx bx-task" + new PluginMenuDef("Agents", link: "page/agent"), // icon: "bx bx-bot" } }); diff --git a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj index 8e79687f..e6df922d 100644 --- a/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj +++ b/src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj @@ -119,6 +119,7 @@ + diff --git a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs index 13885fec..5c215884 100644 --- a/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs +++ b/src/Infrastructure/BotSharp.Core/Conversations/ConversationPlugin.cs @@ -51,7 +51,7 @@ public class ConversationPlugin : IBotSharpPlugin public bool AttachMenu(List menu) { var section = menu.First(x => x.Label == "Apps"); - menu.Add(new PluginMenuDef("Conversation", link: "/page/conversation", icon: "bx bx-conversation", weight: section.Weight + 5)); + menu.Add(new PluginMenuDef("Conversation", link: "page/conversation", icon: "bx bx-conversation", weight: section.Weight + 5)); return true; } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index 8adf711a..fef3c6dc 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -192,20 +192,17 @@ public class BotSharpDbContext : Database, IBotSharpRepository #endregion #region User - public User? GetUserByEmail(string email) - { - throw new NotImplementedException(); - } + public User? GetUserByEmail(string email) + => throw new NotImplementedException(); - public User? GetUserById(string id) - { - throw new NotImplementedException(); - } + public User? GetUserById(string id) + => throw new NotImplementedException(); - public void CreateUser(User user) - { - throw new NotImplementedException(); - } + public User? GetUserByUserName(string userName) + => throw new NotImplementedException(); + + public void CreateUser(User user) + => throw new NotImplementedException(); #endregion diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs index 64c46ad9..bc992a08 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.User.cs @@ -1,31 +1,35 @@ using BotSharp.Abstraction.Users.Models; using System.IO; -namespace BotSharp.Core.Repository +namespace BotSharp.Core.Repository; + +public partial class FileRepository { - public partial class FileRepository + public User? GetUserByEmail(string email) { - public User? GetUserByEmail(string email) - { - return Users.FirstOrDefault(x => x.Email == email); - } + return Users.FirstOrDefault(x => x.Email == email.ToLower()); + } - public User? GetUserById(string id = null) - { - return Users.FirstOrDefault(x => x.ExternalId == id || x.Id == id); - } + public User? GetUserById(string id = null) + { + return Users.FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id)); + } - public void CreateUser(User user) + public User? GetUserByUserName(string userName = null) + { + return Users.FirstOrDefault(x => x.UserName == userName.ToLower()); + } + + public void CreateUser(User user) + { + var userId = Guid.NewGuid().ToString(); + user.Id = userId; + var dir = Path.Combine(_dbSettings.FileRepository, "users", userId); + if (!Directory.Exists(dir)) { - var userId = Guid.NewGuid().ToString(); - user.Id = userId; - 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)); + Directory.CreateDirectory(dir); } + var path = Path.Combine(dir, "user.json"); + File.WriteAllText(path, JsonSerializer.Serialize(user, _options)); } } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index cca7d2e7..b5d4683b 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -4,11 +4,6 @@ using FunctionDef = BotSharp.Abstraction.Functions.Models.FunctionDef; using BotSharp.Abstraction.Users.Models; using BotSharp.Abstraction.Agents.Models; using MongoDB.Driver; -using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Repositories.Filters; -using BotSharp.Abstraction.Repositories.Models; -using BotSharp.Abstraction.Routing.Settings; -using BotSharp.Abstraction.Evaluations.Settings; using System.Text.Encodings.Web; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Statistics.Settings; diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs index 167d5cfe..b9b8b932 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserIdentity.cs @@ -28,4 +28,7 @@ public class UserIdentity : IUserIdentity public string LastName => _claims?.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value!; + + public string FullName + => $"{FirstName} {LastName}".Trim(); } diff --git a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs index 9fd1c676..d0060671 100644 --- a/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs +++ b/src/Infrastructure/BotSharp.Core/Users/Services/UserService.cs @@ -2,6 +2,7 @@ using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Users.Models; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; +using NanoidDotNet; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; @@ -11,24 +12,42 @@ public class UserService : IUserService { private readonly IServiceProvider _services; private readonly IUserIdentity _user; + private readonly ILogger _logger; - public UserService(IServiceProvider services, IUserIdentity user) + public UserService(IServiceProvider services, IUserIdentity user, ILogger logger) { _services = services; _user = user; + _logger = logger; } public async Task CreateUser(User user) { + if (string.IsNullOrEmpty(user.UserName)) + { + // generate unique name + var name = user.Email.Split("@").First() + "-" + Nanoid.Generate("0123456789botsharp", 6); + user.UserName = name; + } + else + { + user.UserName = user.UserName.ToLower(); + } + var db = _services.GetRequiredService(); - var record = db.GetUserByEmail(user.Email); + var record = db.GetUserByUserName(user.UserName); + if (record != null) { return record; } + if (string.IsNullOrEmpty(user.Id)) + { + user.Id = Guid.NewGuid().ToString(); + } + record = user; - record.UserName = user.UserName.ToLower(); record.Email = user.Email.ToLower(); record.Salt = Guid.NewGuid().ToString("N"); record.Password = Utilities.HashText(user.Password, record.Salt); @@ -43,10 +62,52 @@ public class UserService : IUserService public async Task GetToken(string authorization) { var base64 = Encoding.UTF8.GetString(Convert.FromBase64String(authorization)); - var (userEmail, password) = base64.SplitAsTuple(":"); + var (id, password) = base64.SplitAsTuple(":"); var db = _services.GetRequiredService(); - var record = db.GetUserByEmail(userEmail); + var record = id.Contains("@") ? db.GetUserByEmail(id) : db.GetUserByUserName(id); + if (record == null) + { + record = db.GetUserByUserName(id); + } + + if (record == null || record.Source != "internal") + { + // check 3rd party user + var validators = _services.GetServices(); + foreach (var validator in validators) + { + var user = await validator.Authenticate(id, password); + if (user == null) + { + continue; + } + + if (string.IsNullOrEmpty(user.Source) || user.Source == "internal") + { + _logger.LogError($"Please set source name in the Authenticate hook."); + return null; + } + + if (record == null) + { + // create a local user record + record = new User + { + UserName = user.UserName, + Email = user.Email, + FirstName = user.FirstName, + LastName = user.LastName, + Source = user.Source, + ExternalId = user.ExternalId, + Password = user.Password, + }; + await CreateUser(record); + } + break; + } + } + if (record == null) { return default; @@ -70,33 +131,43 @@ public class UserService : IUserService private string GenerateJwtToken(User user) { + var claims = new List + { + new Claim(JwtRegisteredClaimNames.NameId, user.Id), + new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName), + new Claim(JwtRegisteredClaimNames.Email, user.Email), + new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName), + new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName), + new Claim("source", user.Source), + new Claim("external_id", user.ExternalId), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + }; + + var validators = _services.GetServices(); + foreach (var validator in validators) + { + validator.AddClaims(claims); + } + var config = _services.GetRequiredService(); var issuer = config["Jwt:Issuer"]; var audience = config["Jwt:Audience"]; var key = Encoding.ASCII.GetBytes(config["Jwt:Key"]); var tokenDescriptor = new SecurityTokenDescriptor { - Subject = new ClaimsIdentity(new[] - { - new Claim(JwtRegisteredClaimNames.NameId, user.Id), - new Claim(JwtRegisteredClaimNames.Email, user.Email), - new Claim(JwtRegisteredClaimNames.GivenName, user.FirstName), - new Claim(JwtRegisteredClaimNames.FamilyName, user.LastName), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) - }), - Expires = DateTime.UtcNow.AddMinutes(5), + Subject = new ClaimsIdentity(claims), + Expires = DateTime.UtcNow.AddHours(2), Issuer = issuer, Audience = audience, - SigningCredentials = new SigningCredentials - (new SymmetricSecurityKey(key), - SecurityAlgorithms.HmacSha512Signature) + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), + SecurityAlgorithms.HmacSha512Signature) }; var tokenHandler = new JwtSecurityTokenHandler(); var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } - [MemoryCache(10 * 60)] + [MemoryCache(10 * 60, perInstanceCache: true)] public async Task GetMyProfile() { var db = _services.GetRequiredService(); @@ -109,6 +180,16 @@ public class UserService : IUserService { var db = _services.GetRequiredService(); var user = db.GetUserById(id); + if (user == null) + { + user = new User + { + Id = id, + FirstName = "Unknown", + LastName = "Anonymous", + Role = AgentRole.User + }; + } return user; } } diff --git a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid index 32de085b..77faafc2 100644 --- a/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid +++ b/src/Infrastructure/BotSharp.Core/data/agents/01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a/templates/planner_prompt.sequential.liquid @@ -1,2 +1,2 @@ -In order to execute the listed instructions in the order specified by the user. +In order to sequentially execute user needs, What is the next step based on the CONVERSATION? \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 35873b9e..1a9389ef 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -29,7 +29,7 @@ public class AgentController : ControllerBase { AgentIds = new List { id } }); - return agents.Items.First(); + return agents.Items.FirstOrDefault(); } [HttpGet("/agents")] diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index ae65fde9..ec1d75e6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Users.Models; + namespace BotSharp.OpenAPI.Controllers; [Authorize] @@ -66,20 +68,40 @@ public class ConversationController : ControllerBase var history = conv.GetDialogHistory(); var userService = _services.GetRequiredService(); + var agentService = _services.GetRequiredService(); var dialogs = new List(); foreach (var message in history) { - var user = await userService.GetUser(message.SenderId); - - dialogs.Add(new ChatResponseModel + if (message.Role == AgentRole.User) { - ConversationId = conversationId, - MessageId = message.MessageId, - CreatedAt = message.CreatedAt, - Text = message.Content, - Sender = UserViewModel.FromUser(user) - }); + var user = await userService.GetUser(message.SenderId); + + dialogs.Add(new ChatResponseModel + { + ConversationId = conversationId, + MessageId = message.MessageId, + CreatedAt = message.CreatedAt, + Text = message.Content, + Sender = UserViewModel.FromUser(user) + }); + } + else + { + var agent = await agentService.GetAgent(message.CurrentAgentId); + dialogs.Add(new ChatResponseModel + { + ConversationId = conversationId, + MessageId = message.MessageId, + CreatedAt = message.CreatedAt, + Text = message.Content, + Sender = new UserViewModel + { + FirstName = agent.Name, + Role = message.Role, + } + }); + } } return dialogs; diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs index 42299e6f..2c231e16 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/PluginController.cs @@ -36,8 +36,8 @@ public class PluginController : ControllerBase { IsHeader = true }, - new PluginMenuDef("Plugins", link: "/page/plugin", icon: "bx bx-plug", weight: 31), - new PluginMenuDef("Settings", link: "/page/setting", icon: "bx bx-cog", weight: 32), + new PluginMenuDef("Plugins", link: "page/plugin", icon: "bx bx-plug", weight: 31), + new PluginMenuDef("Settings", link: "page/setting", icon: "bx bx-cog", weight: 32), }; var loader = _services.GetRequiredService(); diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs index 1a4706a3..2d7d2922 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Users/UserViewModel.cs @@ -29,9 +29,9 @@ public class UserViewModel { return new UserViewModel { - FirstName = "AI", - LastName = "Assistant", - Role = AgentRole.Assistant + FirstName = "Unknown", + LastName = "Anonymous", + Role = AgentRole.User }; } diff --git a/src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs b/src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs index 572bff90..e136a961 100644 --- a/src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Dashboard/DashboardPlugin.cs @@ -12,7 +12,7 @@ public class DashboardPlugin : IBotSharpPlugin public string Id => "d42a0c21-b461-44f6-ada2-499510d260af"; public string Name => "Dashboard"; public string Description => "Dashboard that offers real-time statistics on model performance, usage trends, and user feedback"; - + public string IconUrl => "https://cdn0.iconfinder.com/data/icons/octicons/1024/dashboard-512.png"; public void RegisterDI(IServiceCollection services, IConfiguration config) { services.AddScoped(); @@ -26,7 +26,7 @@ public class DashboardPlugin : IBotSharpPlugin public bool AttachMenu(List menu) { var section = menu.First(x => x.Label == "Apps"); - menu.Add(new PluginMenuDef("Dashboard", link: "/page/dashboard", icon: "bx bx-home-circle", weight: section.Weight - 1)); + menu.Add(new PluginMenuDef("Dashboard", link: "page/dashboard", icon: "bx bx-home-circle", weight: section.Weight - 1)); return true; } } diff --git a/src/Plugins/BotSharp.Plugin.HuggingFace/Providers/ChatCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.HuggingFace/Providers/ChatCompletionProvider.cs index ed0f78db..88b37dbb 100644 --- a/src/Plugins/BotSharp.Plugin.HuggingFace/Providers/ChatCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.HuggingFace/Providers/ChatCompletionProvider.cs @@ -1,6 +1,5 @@ using BotSharp.Abstraction.Agents; using BotSharp.Abstraction.Agents.Enums; -using BotSharp.Abstraction.Conversations.Settings; using BotSharp.Abstraction.Loggers; using BotSharp.Plugin.HuggingFace.Services; using BotSharp.Plugin.HuggingFace.Settings; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs index 71829d54..c21a6e08 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/UserDocument.cs @@ -1,3 +1,5 @@ +using BotSharp.Abstraction.Users.Models; + namespace BotSharp.Plugin.MongoStorage.Collections; public class UserDocument : MongoBase @@ -8,9 +10,27 @@ public class UserDocument : MongoBase public string Email { get; set; } public string Salt { get; set; } public string Password { get; set; } + public string Source { get; set; } = "internal"; public string? ExternalId { get; set; } public string Role { get; set; } public DateTime CreatedTime { get; set; } public DateTime UpdatedTime { get; set; } + + public User ToUser() + { + return new User + { + Id = Id, + UserName = UserName, + FirstName = FirstName, + LastName = LastName, + Email = Email, + Password = Password, + Salt = Salt, + Source = Source, + ExternalId = ExternalId, + Role = Role + }; + } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs index 619e78c5..a7a574fa 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoStoragePlugin.cs @@ -33,7 +33,7 @@ public class MongoStoragePlugin : IBotSharpPlugin public bool AttachMenu(List menu) { var section = menu.First(x => x.Label == "Apps"); - menu.Add(new PluginMenuDef("MongoDB", icon: "bx bx-data", link: "/page/mongodb", weight: section.Weight + 10)); + menu.Add(new PluginMenuDef("MongoDB", icon: "bx bx-data", link: "page/mongodb", weight: section.Weight + 10)); return true; } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs index c9fc4539..1b6b8b77 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.User.cs @@ -7,36 +7,21 @@ public partial class MongoRepository { public User? GetUserByEmail(string email) { - var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Email == email); - return user != null ? new User - { - Id = user.Id, - UserName = user.UserName, - FirstName = user.FirstName, - LastName = user.LastName, - Email = user.Email, - Password = user.Password, - Salt = user.Salt, - ExternalId = user.ExternalId, - Role = user.Role - } : null; + var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Email == email.ToLower()); + return user != null ? user.ToUser() : null; } public User? GetUserById(string id) { - var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == id || x.ExternalId == id); - return user != null ? new User - { - Id = user.Id, - UserName = user.UserName, - FirstName = user.FirstName, - LastName = user.LastName, - Email = user.Email, - Password = user.Password, - Salt = user.Salt, - ExternalId = user.ExternalId, - Role = user.Role - } : null; + var user = _dc.Users.AsQueryable() + .FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id)); + return user != null ? user.ToUser() : null; + } + + public User? GetUserByUserName(string userName) + { + var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.UserName == userName.ToLower()); + return user != null ? user.ToUser() : null; } public void CreateUser(User user) @@ -45,13 +30,14 @@ public partial class MongoRepository var userCollection = new UserDocument { - Id = Guid.NewGuid().ToString(), + Id = user.Id ?? Guid.NewGuid().ToString(), UserName = user.UserName, FirstName = user.FirstName, LastName = user.LastName, Salt = user.Salt, Password = user.Password, Email = user.Email, + Source = user.Source, ExternalId = user.ExternalId, Role = user.Role, CreatedTime = DateTime.UtcNow, diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs index 75e8ad6a..476e926e 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ChangeListValue.cs @@ -1,12 +1,10 @@ -using BotSharp.Plugin.WebDriver.Services; -using System.Threading; - namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { public async Task ChangeListValue(Agent agent, BrowsingContextIn context, string messageId) { + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); // Retrieve the page raw html and infer the element path var body = await _instance.Page.QuerySelectorAsync("body"); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs index 82e9683d..294e6894 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ClickButton.cs @@ -4,6 +4,7 @@ public partial class PlaywrightWebDriver { public async Task ClickElement(Agent agent, BrowsingContextIn context, string messageId) { + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); var driverService = _services.GetRequiredService(); // Retrieve the page raw html and infer the element path @@ -37,5 +38,6 @@ public partial class PlaywrightWebDriver messageId); ILocator element = Locator(htmlElementContextOut); await element.ClickAsync(); + await _instance.Page.WaitForLoadStateAsync(LoadState.NetworkIdle); } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs new file mode 100644 index 00000000..7538af77 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.CloseBrowser.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.Logging; + +namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; + +public partial class PlaywrightWebDriver +{ + public async Task CloseBrowser(Agent agent, BrowsingContextIn context, string messageId) + { + // await _instance.Browser.CloseAsync(); + _logger.LogInformation($"{agent.Name} closed browser with page {_instance.Page.Url}"); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs index 0ffc9109..2134e631 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.ExtractData.cs @@ -1,11 +1,11 @@ -using BotSharp.Plugin.WebDriver.Services; - namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { public async Task ExtractData(Agent agent, BrowsingContextIn context, string messageId) { + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + // Retrieve the page raw html and infer the element path var body = await _instance.Page.QuerySelectorAsync("body"); var content = await body.InnerTextAsync(); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs new file mode 100644 index 00000000..09b9deb7 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.GoToPage.cs @@ -0,0 +1,10 @@ +namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; + +public partial class PlaywrightWebDriver +{ + public async Task GoToPage(Agent agent, BrowsingContextIn context, string messageId) + { + await _instance.Page.GotoAsync(context.Url); + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs index 499c953e..6bbeef6d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserPassword.cs @@ -1,11 +1,11 @@ -using Microsoft.Extensions.Configuration; - namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { public async Task InputUserPassword(Agent agent, BrowsingContextIn context, string messageId) { + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + // Retrieve the page raw html and infer the element path var body = await _instance.Page.QuerySelectorAsync("body"); diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs index 5362602f..28a652b7 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.InputUserText.cs @@ -4,6 +4,8 @@ public partial class PlaywrightWebDriver { public async Task InputUserText(Agent agent, BrowsingContextIn context, string messageId) { + await _instance.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); + // await _instance.Page.WaitForLoadStateAsync(LoadState.NetworkIdle); var driverService = _services.GetRequiredService(); // Retrieve the page raw html and infer the element path @@ -17,12 +19,15 @@ public partial class PlaywrightWebDriver var id = await input.GetAttributeAsync("id"); var name = await input.GetAttributeAsync("name"); var type = await input.GetAttributeAsync("type"); + var placeholder = await input.GetAttributeAsync("placeholder"); + str.Add(driverService.AssembleMarkup("input", new MarkupProperties { Id = id, Name = name, Type = type, - Text = text + Text = text, + Placeholder = placeholder })); } @@ -33,12 +38,14 @@ public partial class PlaywrightWebDriver var id = await input.GetAttributeAsync("id"); var name = await input.GetAttributeAsync("name"); var type = await input.GetAttributeAsync("type"); + var placeholder = await input.GetAttributeAsync("placeholder"); str.Add(driverService.AssembleMarkup("textarea", new MarkupProperties { Id = id, Name = name, Type = type, - Text = text + Text = text, + Placeholder = placeholder })); } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs index d095d776..e6401795 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.LaunchBrowser.cs @@ -14,6 +14,7 @@ public partial class PlaywrightWebDriver }); _instance.SetPage(page); var response = await page.GotoAsync(url); + await page.WaitForLoadStateAsync(LoadState.DOMContentLoaded); } return _instance.Browser; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs index c513bf1c..8adfdc39 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Drivers/PlaywrightDriver/PlaywrightWebDriver.cs @@ -1,15 +1,19 @@ +using Microsoft.Extensions.Logging; + namespace BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; public partial class PlaywrightWebDriver { private readonly IServiceProvider _services; private readonly PlaywrightInstance _instance; + private readonly ILogger _logger; public PlaywrightInstance Instance => _instance; - public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance) + public PlaywrightWebDriver(IServiceProvider services, PlaywrightInstance instance, ILogger logger) { _services = services; _instance = instance; + _logger = logger; } private ILocator Locator(HtmlElementContextOut context) @@ -20,6 +24,23 @@ public partial class PlaywrightWebDriver // await _instance.Page.WaitForSelectorAsync($"#{htmlElementContextOut.ElementId}", new PageWaitForSelectorOptions { Timeout = 3 }); element = _instance.Page.Locator($"#{context.ElementId}"); } + else if (!string.IsNullOrEmpty(context.ElementName)) + { + var role = context.TagName switch + { + "input" => AriaRole.Textbox, + "textarea" => AriaRole.Textbox, + "button" => AriaRole.Button, + _ => AriaRole.Generic + }; + // await _instance.Page.WaitForSelectorAsync($"#{htmlElementContextOut.ElementId}", new PageWaitForSelectorOptions { Timeout = 3 }); + element = _instance.Page.Locator($"[name='{context.ElementName}']"); + + if (element.CountAsync().Result == 0) + { + _logger.LogError($"Can't locate element {role} {context.ElementName}"); + } + } else { if (context.Index < 0) diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs index 1002351b..ac4c9116 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ChangeListValueFn.cs @@ -23,10 +23,9 @@ public class ChangeListValueFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.ChangeListValue(agent, args, message.MessageId); - message.Content = $"Updat the value of \"${args.ElementName}\" to \"{args.UpdateValue}\" successfully."; + message.Content = $"Updat the value of \"{args.ElementName}\" to \"{args.UpdateValue}\" successfully."; return true; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs index afa8da5d..3ecbf6b1 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ClickButtonFn.cs @@ -23,7 +23,6 @@ public class ClickButtonFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.ClickElement(agent, args, message.MessageId); message.Content = $"Click button {args.ElementName} successfully."; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CloseBrowserFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CloseBrowserFn.cs new file mode 100644 index 00000000..1e10e084 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/CloseBrowserFn.cs @@ -0,0 +1,28 @@ +using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; + +namespace BotSharp.Plugin.WebDriver.Functions; + +public class CloseBrowserFn : IFunctionCallback +{ + public string Name => "close_browser"; + + private readonly IServiceProvider _services; + private readonly PlaywrightWebDriver _driver; + + public CloseBrowserFn(IServiceProvider services, + PlaywrightWebDriver driver) + { + _services = services; + _driver = driver; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.CloseBrowser(agent, args, message.MessageId); + message.Content = $"Browser is closed"; + return true; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs index 71c3fa39..271476f0 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/ExtractDataFn.cs @@ -1,5 +1,3 @@ - -using BotSharp.Abstraction.Agents; using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; namespace BotSharp.Plugin.WebDriver.Functions; @@ -23,7 +21,6 @@ public class ExtractDataFn : IFunctionCallback var args = JsonSerializer.Deserialize(message.FunctionArgs); var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); message.Content = await _driver.ExtractData(agent, args, message.MessageId); return true; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs new file mode 100644 index 00000000..b2576359 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/GoToPageFn.cs @@ -0,0 +1,28 @@ +using BotSharp.Plugin.WebDriver.Drivers.PlaywrightDriver; + +namespace BotSharp.Plugin.WebDriver.Functions; + +public class GoToPageFn : IFunctionCallback +{ + public string Name => "go_to_page"; + + private readonly IServiceProvider _services; + private readonly PlaywrightWebDriver _driver; + + public GoToPageFn(IServiceProvider services, + PlaywrightWebDriver driver) + { + _services = services; + _driver = driver; + } + + public async Task Execute(RoleDialogModel message) + { + var args = JsonSerializer.Deserialize(message.FunctionArgs); + var agentService = _services.GetRequiredService(); + var agent = await agentService.LoadAgent(message.CurrentAgentId); + await _driver.GoToPage(agent, args, message.MessageId); + message.Content = $"Page {args.Url} is open."; + return true; + } +} diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs index ccafd721..30aae332 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserPasswordFn.cs @@ -23,7 +23,6 @@ public class InputUserPasswordFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.InputUserPassword(agent, args, message.MessageId); message.Content = "Input password successfully"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs index 856203ed..9248d878 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Functions/InputUserTextFn.cs @@ -23,7 +23,6 @@ public class InputUserTextFn : IFunctionCallback var agentService = _services.GetRequiredService(); var agent = await agentService.LoadAgent(message.CurrentAgentId); - await _driver.Instance.Page.WaitForLoadStateAsync(LoadState.Load); await _driver.InputUserText(agent, args, message.MessageId); message.Content = $"Input text \"{args.InputText}\" successfully."; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs index a0fbfaf6..0cfbb6aa 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/LlmContexts/HtmlElementContextOut.cs @@ -7,6 +7,9 @@ public class HtmlElementContextOut [JsonPropertyName("element_id")] public string ElementId { get; set; } + [JsonPropertyName("element_name")] + public string ElementName { get; set; } + [JsonPropertyName("tag_name")] public string TagName { get; set; } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Models/MarkupProperties.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Models/MarkupProperties.cs index 410bf50c..0f38094f 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Models/MarkupProperties.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Models/MarkupProperties.cs @@ -6,4 +6,5 @@ internal class MarkupProperties public string? Name { get; set; } public string? Type { get; set; } public string? Text { get; set; } + public string? Placeholder { get; set; } } diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Services/WebDriverService.AssembleMarkup.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Services/WebDriverService.AssembleMarkup.cs index 14eb5c6c..e59cf1df 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Services/WebDriverService.AssembleMarkup.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Services/WebDriverService.AssembleMarkup.cs @@ -22,6 +22,11 @@ public partial class WebDriverService html += $" type=\"{properties.Type}\""; } + if (!string.IsNullOrEmpty(properties.Placeholder)) + { + html += $" placeholder=\"{properties.Placeholder}\""; + } + if (!string.IsNullOrEmpty(properties.Text)) { html += $">{properties.Text}"; diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs index 9a0e6ecc..5cdc2eea 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs +++ b/src/Plugins/BotSharp.Plugin.WebDriver/Using.cs @@ -6,6 +6,7 @@ global using BotSharp.Abstraction.Plugins; global using System.Text.Json; global using BotSharp.Abstraction.Conversations.Models; global using Microsoft.Playwright; +global using Microsoft.Extensions.Configuration; global using BotSharp.Plugin.WebDriver.Drivers; global using System.Threading.Tasks; global using BotSharp.Abstraction.Functions; @@ -14,6 +15,7 @@ global using BotSharp.Abstraction.Templating; global using BotSharp.Plugin.WebDriver.LlmContexts; global using Microsoft.Extensions.DependencyInjection; global using System.Linq; +global using BotSharp.Abstraction.Agents; global using BotSharp.Abstraction.Utilities; global using BotSharp.Plugin.WebDriver.Models; global using BotSharp.Plugin.WebDriver.Services; \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json index 2d928f12..c87e2a3b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/agent.json @@ -8,6 +8,6 @@ "isPublic": true, "profiles": [ "web-driver" ], "llmConfig": { - "max_recursion_depth": 5 + "max_recursion_depth": 10 } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json index 87723841..59a678aa 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/functions.json @@ -1,98 +1,122 @@ -[ - { - "name": "open_browser", - "description": "open a browser", - "parameters": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "website url starts with https://" - } - }, - "required": ["url"] - } - }, - { - "name": "click_button", - "description": "Click a button in a web page.", - "parameters": { - "type": "object", - "properties": { - "element_name": { - "type": "string", - "description": "the html element name." - } - }, - "required": ["element_name"] - } - }, - { - "name": "extract_data_from_page", - "description": "Extract data from current web page.", - "parameters": { - "type": "object", - "properties": { - "question": { - "type": "string", - "description": "the information user wants to know" - } - }, - "required": ["question"] - } - }, - { - "name": "input_user_text", - "description": "Input non-sensitive text in current web page.", - "parameters": { - "type": "object", - "properties": { - "element_name": { - "type": "string", - "description": "the html input box element name." - }, - "input_text": { - "type": "string", - "description": "non-sensitive text user provided." - }, - "press_enter": { - "type": "boolean", - "description": "whether to press ENTER" - } - }, - "required": ["element_name", "input_text"] - } - }, - { - "name": "change_list_value", - "description": "Update value from dropdown list or radio button", - "parameters": { - "type": "object", - "properties": { - "element_name": { - "type": "string", - "description": "the html selection element name." - }, - "update_value": { - "type": "string", - "description": "the value in the list." - } - }, - "required": ["element_name", "update_value"] - } - }, - { - "name": "input_user_password", - "description": "Input password in current web page", - "parameters": { - "type": "object", - "properties": { - "password": { - "type": "string", - "description": "user password" - } - }, - "required": ["password"] +[ + { + "name": "open_browser", + "description": "open a browser", + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "website url starts with https://" } + }, + "required": [ "url" ] } + }, + { + "name": "close_browser", + "description": "Close browser", + "parameters": { + "type": "object", + "properties": { + }, + "required": [] + } + }, + { + "name": "go_to_page", + "description": "go to another page", + "parameters": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "page url start with https://" + } + }, + "required": [ "url" ] + } + }, + { + "name": "click_button", + "description": "Click a button in a web page.", + "parameters": { + "type": "object", + "properties": { + "element_name": { + "type": "string", + "description": "the html element name." + } + }, + "required": [ "element_name" ] + } + }, + { + "name": "extract_data_from_page", + "description": "Extract data from current web page.", + "parameters": { + "type": "object", + "properties": { + "question": { + "type": "string", + "description": "the information user wants to know" + } + }, + "required": [ "question" ] + } + }, + { + "name": "input_user_text", + "description": "Input non-sensitive text in current web page.", + "parameters": { + "type": "object", + "properties": { + "element_name": { + "type": "string", + "description": "the html input box element name." + }, + "input_text": { + "type": "string", + "description": "non-sensitive text user provided." + }, + "press_enter": { + "type": "boolean", + "description": "whether to press ENTER" + } + }, + "required": [ "element_name", "input_text" ] + } + }, + { + "name": "change_list_value", + "description": "Update value from dropdown list or radio button", + "parameters": { + "type": "object", + "properties": { + "element_name": { + "type": "string", + "description": "the html selection element name." + }, + "update_value": { + "type": "string", + "description": "the value in the list." + } + }, + "required": [ "element_name", "update_value" ] + } + }, + { + "name": "input_user_password", + "description": "Input password in current web page", + "parameters": { + "type": "object", + "properties": { + "password": { + "type": "string", + "description": "user password" + } + }, + "required": [ "password" ] + } + } ] diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid index c41ef235..82c9303d 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/instruction.liquid @@ -7,4 +7,5 @@ Follow below steps to response: Additional response requirements: * Call function input_user_password if user wants to input password. -* Don't do extra steps if user didn't ask. \ No newline at end of file +* Don't do extra steps if user didn't ask. +* Don't miss any steps. \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid index c1991861..d037018b 100644 --- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid +++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/f3ae2a0f-e6ba-4ee1-a0b9-75d7431ff32b/templates/html_parser.liquid @@ -2,5 +2,5 @@ === According to above HTML === Find the html element in the similar meaning of "{{ element_name }}". -Output in JSON format {"tag_name": "", "element_id": "populated if element has id", "index": -1} with appropriate values. +Output in JSON format {"tag_name": "", "element_id": "populated if element has id", "element_name": "populated if element has name", "index": -1}. The index is the position of the element which starts with 0. \ No newline at end of file diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index 54aa1fac..9ff26d27 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -82,7 +82,7 @@ "EnableLlmCompletionLog": false, "EnableExecutionLog": true }, - "Stats": { + "Statistics": { "DataDir": "stats" }, "LlamaSharp": { diff --git a/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json b/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json index e57ba894..d05e0481 100644 --- a/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json +++ b/tests/BotSharp.Plugin.PizzaBot/data/agents/8970b1e5-d260-4e2c-90b1-f1415a257c18/agent.json @@ -3,6 +3,7 @@ "name": "Pizza Bot", "description": "AI assistant that can help customer place pizza order.", "type": "routing", + "inheritAgentId": "01fcc3e5-9af7-49e6-ad7a-a760bd12dc4a", "createdDateTime": "2023-08-18T10:39:32.2349685Z", "updatedDateTime": "2023-08-18T14:39:32.2349686Z", "iconUrl": "https://cdn-icons-png.flaticon.com/512/6978/6978255.png",