Merge branch 'master' into master
This commit is contained in:
commit
b7d4ccae54
13
BotSharp.sln
13
BotSharp.sln
|
|
@ -117,7 +117,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.Graph", "sr
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Plugin.AudioHandler", "src\Plugins\BotSharp.Plugin.AudioHandler\BotSharp.Plugin.AudioHandler.csproj", "{F57F4862-F8D4-44A1-AC12-5C131B5C9785}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Core.SideCar", "src\Infrastructure\BotSharp.Core.SideCar\BotSharp.Core.SideCar.csproj", "{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotSharp.Core.SideCar", "src\Infrastructure\BotSharp.Core.SideCar\BotSharp.Core.SideCar.csproj", "{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BotSharp.Plugin.VertexAI", "src\Plugins\BotSharp.Plugin.LangChain\BotSharp.Plugin.VertexAI.csproj", "{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -479,6 +481,14 @@ Global
|
|||
{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -536,6 +546,7 @@ Global
|
|||
{EBFE97DA-D0BA-48BA-8B5D-083B60348D1D} = {97A0B191-64D7-4F8A-BFE8-1BFCC5E247E1}
|
||||
{F57F4862-F8D4-44A1-AC12-5C131B5C9785} = {51AFE054-AE99-497D-A593-69BAEFB5106F}
|
||||
{6D3A54F9-4792-41DB-BE7D-4F7B1D918EAE} = {E29DC6C4-5E57-48C5-BCB0-6B8F84782749}
|
||||
{7DA2DCD0-551B-432E-AA5C-22DDD3ED459B} = {D5293208-2BEF-42FC-A64C-5954F61720BA}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A9969D89-C98B-40A5-A12B-FC87E55B3A19}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public interface IKnowledgeService
|
|||
/// <param name="collectionName"></param>
|
||||
/// <param name="files"></param>
|
||||
/// <returns></returns>
|
||||
Task<UploadKnowledgeResponse> UploadDocumentsToKnowledge(string collectionName, IEnumerable<ExternalFileModel> files);
|
||||
Task<UploadKnowledgeResponse> UploadDocumentsToKnowledge(string collectionName, IEnumerable<ExternalFileModel> files, ChunkOption? option = null);
|
||||
/// <summary>
|
||||
/// Save document content to knowledgebase without saving the document
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -13,4 +13,15 @@ public class ChunkOption
|
|||
public int Conjunction { get; set; }
|
||||
|
||||
public bool SplitByWord { get; set; }
|
||||
|
||||
|
||||
public static ChunkOption Default()
|
||||
{
|
||||
return new ChunkOption
|
||||
{
|
||||
Size = 1024,
|
||||
Conjunction = 12,
|
||||
SplitByWord = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,5 +4,6 @@ namespace BotSharp.Abstraction.Plugins.Models
|
|||
{
|
||||
public Pagination Pager { get; set; } = new Pagination();
|
||||
public IEnumerable<string>? Names { get; set; }
|
||||
public string? SimilarName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,15 @@ public class AgentFilter
|
|||
{
|
||||
public Pagination Pager { get; set; } = new Pagination();
|
||||
public string? AgentName { get; set; }
|
||||
public string? SimilarName { get; set; }
|
||||
public bool? Disabled { get; set; }
|
||||
public bool? Installed { get; set; }
|
||||
public string? Type { get; set; }
|
||||
public bool? IsPublic { get; set; }
|
||||
public List<string>? AgentIds { get; set; }
|
||||
|
||||
public static AgentFilter Empty()
|
||||
{
|
||||
return new AgentFilter();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,9 @@ public class AgentTaskFilter
|
|||
public Pagination Pager { get; set; } = new Pagination();
|
||||
public string? AgentId { get; set; }
|
||||
public bool? Enabled { get; set; }
|
||||
|
||||
public static AgentTaskFilter Empty()
|
||||
{
|
||||
return new AgentTaskFilter();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,9 @@ public class ConversationFilter
|
|||
public IEnumerable<KeyValue>? States { get; set; } = [];
|
||||
|
||||
public IEnumerable<string>? Tags { get; set; } = [];
|
||||
|
||||
public static ConversationFilter Empty()
|
||||
{
|
||||
return new ConversationFilter();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
using BotSharp.Abstraction.Users.Enums;
|
||||
|
||||
namespace BotSharp.Abstraction.Repositories.Filters;
|
||||
|
||||
public class RoleFilter
|
||||
{
|
||||
[JsonPropertyName("names")]
|
||||
public IEnumerable<string>? Names { get; set; }
|
||||
|
||||
[JsonPropertyName("exclude_roles")]
|
||||
public IEnumerable<string>? ExcludeRoles { get; set; } = UserConstant.AdminRoles;
|
||||
|
||||
|
||||
public static RoleFilter Empty()
|
||||
{
|
||||
return new RoleFilter();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
namespace BotSharp.Abstraction.Users.Models;
|
||||
namespace BotSharp.Abstraction.Repositories.Filters;
|
||||
|
||||
public class UserFilter : Pagination
|
||||
{
|
||||
|
|
@ -14,6 +14,14 @@ public class UserFilter : Pagination
|
|||
[JsonPropertyName("roles")]
|
||||
public IEnumerable<string>? Roles { get; set; }
|
||||
|
||||
[JsonPropertyName("types")]
|
||||
public IEnumerable<string>? Types { get; set; }
|
||||
|
||||
[JsonPropertyName("sources")]
|
||||
public IEnumerable<string>? Sources { get; set; }
|
||||
|
||||
public static UserFilter Empty()
|
||||
{
|
||||
return new UserFilter();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using BotSharp.Abstraction.Loggers.Models;
|
||||
using BotSharp.Abstraction.Plugins.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Roles.Models;
|
||||
using BotSharp.Abstraction.Shared;
|
||||
using BotSharp.Abstraction.Tasks.Models;
|
||||
using BotSharp.Abstraction.Translation.Models;
|
||||
|
|
@ -16,6 +17,13 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
|||
void SavePluginConfig(PluginConfig config);
|
||||
#endregion
|
||||
|
||||
#region Role
|
||||
bool RefreshRoles(IEnumerable<Role> roles) => throw new NotImplementedException();
|
||||
IEnumerable<Role> GetRoles(RoleFilter filter) => throw new NotImplementedException();
|
||||
Role? GetRoleDetails(string roleId, bool includeAgent = false) => throw new NotImplementedException();
|
||||
bool UpdateRole(Role role, bool updateRoleAgents = false) => throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region User
|
||||
User? GetUserByEmail(string email) => throw new NotImplementedException();
|
||||
User? GetUserByPhone(string phone, string role = null, string regionCode = "CN") => throw new NotImplementedException();
|
||||
|
|
@ -34,7 +42,8 @@ public interface IBotSharpRepository : IHaveServiceProvider
|
|||
void UpdateUserIsDisable(string userId, bool isDisable) => throw new NotImplementedException();
|
||||
void UpdateUsersIsDisable(List<string> userIds, bool isDisable) => throw new NotImplementedException();
|
||||
PagedItems<User> GetUsers(UserFilter filter) => throw new NotImplementedException();
|
||||
bool UpdateUser(User user, bool isUpdateUserAgents = false) => throw new NotImplementedException();
|
||||
User? GetUserDetails(string userId, bool includeAgent = false) => throw new NotImplementedException();
|
||||
bool UpdateUser(User user, bool updateUserAgents = false) => throw new NotImplementedException();
|
||||
#endregion
|
||||
|
||||
#region Agent
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
namespace BotSharp.Abstraction.Roles;
|
||||
|
||||
public interface IRoleService
|
||||
{
|
||||
Task<bool> RefreshRoles();
|
||||
Task<IEnumerable<string>> GetRoleOptions();
|
||||
Task<IEnumerable<Role>> GetRoles(RoleFilter filter);
|
||||
Task<Role?> GetRoleDetails(string roleId, bool includeAgent = false);
|
||||
Task<bool> UpdateRole(Role role, bool isUpdateRoleAgents = false);
|
||||
}
|
||||
27
src/Infrastructure/BotSharp.Abstraction/Roles/Models/Role.cs
Normal file
27
src/Infrastructure/BotSharp.Abstraction/Roles/Models/Role.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
namespace BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
public class Role
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("permissions")]
|
||||
public IEnumerable<string> Permissions { get; set; } = [];
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<RoleAgentAction> AgentActions { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("updated_time")]
|
||||
public DateTime UpdatedTime { get; set; }
|
||||
|
||||
[JsonPropertyName("created_time")]
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
namespace BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
public class RoleAgent
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("user_id")]
|
||||
public string RoleId { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("agent_id")]
|
||||
public string AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("actions")]
|
||||
public IEnumerable<string> Actions { get; set; } = [];
|
||||
|
||||
[JsonIgnore]
|
||||
public Agent? Agent { get; set; }
|
||||
|
||||
[JsonPropertyName("updated_time")]
|
||||
public DateTime UpdatedTime { get; set; }
|
||||
|
||||
[JsonPropertyName("created_time")]
|
||||
public DateTime CreatedTime { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
namespace BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
public class RoleAgentAction
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonPropertyName("agent_id")]
|
||||
public string AgentId { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Agent? Agent { get; set; }
|
||||
|
||||
[JsonPropertyName("actions")]
|
||||
public IEnumerable<string> Actions { get; set; } = [];
|
||||
}
|
||||
|
|
@ -4,4 +4,6 @@ public static class UserAction
|
|||
{
|
||||
public const string Edit = "edit";
|
||||
public const string Chat = "chat";
|
||||
public const string Train = "train";
|
||||
public const string Evaluate = "evaluate";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,23 @@ namespace BotSharp.Abstraction.Users.Enums;
|
|||
|
||||
public class UserRole
|
||||
{
|
||||
public const string Root = "root";
|
||||
|
||||
/// <summary>
|
||||
/// Admin account
|
||||
/// </summary>
|
||||
public const string Admin = "admin";
|
||||
|
||||
/// <summary>
|
||||
/// Customer service representative (CSR)
|
||||
/// </summary>
|
||||
public const string CSR = "csr";
|
||||
|
||||
/// <summary>
|
||||
/// Authorized user
|
||||
/// </summary>
|
||||
public const string User = "user";
|
||||
|
||||
/// <summary>
|
||||
/// Customer service representative (CSR)
|
||||
/// </summary>
|
||||
public const string CSR = "csr";
|
||||
|
||||
/// <summary>
|
||||
/// Back office operations
|
||||
/// </summary>
|
||||
|
|
@ -33,6 +35,4 @@ public class UserRole
|
|||
/// AI Assistant
|
||||
/// </summary>
|
||||
public const string Assistant = "assistant";
|
||||
|
||||
public const string Root = "root";
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
using BotSharp.OpenAPI.ViewModels.Users;
|
||||
|
||||
|
|
@ -7,7 +8,10 @@ public interface IUserService
|
|||
{
|
||||
Task<User> GetUser(string id);
|
||||
Task<PagedItems<User>> GetUsers(UserFilter filter);
|
||||
Task<bool> UpdateUser(User model, bool isUpdateUserAgents = false);
|
||||
Task<User?> GetUserDetails(string userId, bool includeAgent = false);
|
||||
Task<bool> IsAdminUser(string userId);
|
||||
Task<UserAuthorization> GetUserAuthorizations(IEnumerable<string>? agentIds = null);
|
||||
Task<bool> UpdateUser(User user, bool isUpdateUserAgents = false);
|
||||
Task<User> CreateUser(User user);
|
||||
Task<Token> ActiveUser(UserActivationModel model);
|
||||
Task<Token?> GetAffiliateToken(string authorization);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
namespace BotSharp.Abstraction.Users.Models;
|
||||
|
||||
public class UserAuthorization
|
||||
{
|
||||
public bool IsAdmin { get; set; }
|
||||
public IEnumerable<string> Permissions { get; set; } = [];
|
||||
public IEnumerable<UserAgent> AgentActions { get; set; } = [];
|
||||
}
|
||||
|
||||
|
||||
public static class UserAuthorizationExtension
|
||||
{
|
||||
public static bool IsAgentActionAllowed(this UserAuthorization auth, string agentId, string targetAction)
|
||||
{
|
||||
if (auth == null || string.IsNullOrEmpty(agentId)) return false;
|
||||
|
||||
if (auth.IsAdmin) return true;
|
||||
|
||||
var found = auth.AgentActions.FirstOrDefault(x => x.AgentId == agentId);
|
||||
if (found == null) return false;
|
||||
|
||||
var actions = found.Actions ?? [];
|
||||
return actions.Any(x => x == targetAction);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ namespace BotSharp.Core.SideCar;
|
|||
public class BotSharpSideCarPlugin : IBotSharpPlugin
|
||||
{
|
||||
public string Id => "06e5a276-bba0-45af-9625-889267c341c9";
|
||||
public string Name => "Side car";
|
||||
public string Name => "Side Car";
|
||||
public string Description => "Provides side car for calling agent cluster in conversation";
|
||||
|
||||
public SettingsMeta Settings => new SettingsMeta("SideCar");
|
||||
|
|
|
|||
|
|
@ -25,8 +25,11 @@ public partial class AgentService
|
|||
var agentSettings = _services.GetRequiredService<AgentSettings>();
|
||||
|
||||
var user = _db.GetUserById(_user.Id);
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var auth = await userService.GetUserAuthorizations();
|
||||
|
||||
_db.BulkInsertAgents(new List<Agent> { agentRecord });
|
||||
if (!UserConstant.AdminRoles.Contains(user.Role))
|
||||
if (auth.IsAdmin || auth.Permissions.Contains(UserPermission.CreateAgent))
|
||||
{
|
||||
_db.BulkInsertUserAgents(new List<UserAgent>
|
||||
{
|
||||
|
|
@ -34,7 +37,7 @@ public partial class AgentService
|
|||
{
|
||||
UserId = user.Id,
|
||||
AgentId = agentRecord.Id,
|
||||
Actions = new List<string> { UserAction.Edit, UserAction.Chat },
|
||||
Actions = new List<string> { UserAction.Edit, UserAction.Train, UserAction.Evaluate, UserAction.Chat },
|
||||
CreatedTime = DateTime.UtcNow,
|
||||
UpdatedTime = DateTime.UtcNow
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using BotSharp.Abstraction.Users.Enums;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
||||
|
|
@ -6,11 +7,10 @@ public partial class AgentService
|
|||
{
|
||||
public async Task<bool> DeleteAgent(string id)
|
||||
{
|
||||
var user = _db.GetUserById(_user.Id);
|
||||
var userAgents = await GetUserAgents(user?.Id);
|
||||
var found = userAgents?.FirstOrDefault(x => x.AgentId == id);
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var auth = await userService.GetUserAuthorizations(new List<string> { id });
|
||||
|
||||
if (!UserConstant.AdminRoles.Contains(user?.Role) && (found?.Actions == null || !found.Actions.Contains(UserAction.Edit)))
|
||||
if (!auth.IsAgentActionAllowed(id, UserAction.Edit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using BotSharp.Abstraction.Repositories.Enums;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
|
@ -17,8 +16,9 @@ public partial class AgentService
|
|||
return refreshResult;
|
||||
}
|
||||
|
||||
var user = _db.GetUserById(_user.Id);
|
||||
if (!UserConstant.AdminRoles.Contains(user.Role))
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var isValid = await userService.IsAdminUser(_user.Id);
|
||||
if (!isValid)
|
||||
{
|
||||
return "Unauthorized user.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using BotSharp.Abstraction.Repositories.Enums;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Agents.Services;
|
||||
|
|
@ -12,12 +13,10 @@ public partial class AgentService
|
|||
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
|
||||
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
var auth = await userService.GetUserAuthorizations(new List<string> { agent.Id });
|
||||
var allowEdit = auth.IsAgentActionAllowed(agent.Id, UserAction.Edit);
|
||||
|
||||
var userAgents = await GetUserAgents(user.Id);
|
||||
var found = userAgents?.FirstOrDefault(x => x.AgentId == agent.Id);
|
||||
|
||||
if (!UserConstant.AdminRoles.Contains(user?.Role) && (found?.Actions == null || found.Actions.Contains(UserAction.Edit)))
|
||||
if (!allowEdit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using BotSharp.Abstraction.Infrastructures;
|
|||
using BotSharp.Core.Processors;
|
||||
using StackExchange.Redis;
|
||||
using BotSharp.Core.Infrastructures.Events;
|
||||
using BotSharp.Core.Roles.Services;
|
||||
|
||||
namespace BotSharp.Core;
|
||||
|
||||
|
|
@ -25,6 +26,7 @@ public static class BotSharpCoreExtensions
|
|||
services.AddSingleton<DistributedLocker>();
|
||||
|
||||
services.AddScoped<ISettingService, SettingService>();
|
||||
services.AddScoped<IRoleService, RoleService>();
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
services.AddScoped<ProcessorFactory>();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Microsoft.Extensions.Configuration;
|
|||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
|
||||
namespace BotSharp.Core.Plugins;
|
||||
|
|
@ -132,6 +133,12 @@ public class PluginLoader
|
|||
plugins = plugins.Where(x => filter.Names.Any(n => x.Name.IsEqualTo(n))).ToList();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.SimilarName))
|
||||
{
|
||||
var regex = new Regex(filter.SimilarName, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
plugins = plugins.Where(x => regex.IsMatch(x.Name)).ToList();
|
||||
}
|
||||
|
||||
return new PagedItems<PluginDef>
|
||||
{
|
||||
Items = plugins.Skip(pager.Offset).Take(pager.Size),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BotSharp.Core.Repository
|
||||
{
|
||||
|
|
@ -65,6 +63,8 @@ namespace BotSharp.Core.Repository
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_agents = [];
|
||||
}
|
||||
|
||||
#region Update Agent Fields
|
||||
|
|
@ -358,12 +358,23 @@ namespace BotSharp.Core.Repository
|
|||
|
||||
public List<Agent> GetAgents(AgentFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = AgentFilter.Empty();
|
||||
}
|
||||
|
||||
var query = Agents;
|
||||
if (!string.IsNullOrEmpty(filter.AgentName))
|
||||
{
|
||||
query = query.Where(x => x.Name.ToLower() == filter.AgentName.ToLower());
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.SimilarName))
|
||||
{
|
||||
var regex = new Regex(filter.SimilarName, RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
query = query.Where(x => regex.IsMatch(x.Name));
|
||||
}
|
||||
|
||||
if (filter.Disabled.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.Disabled == filter.Disabled);
|
||||
|
|
@ -476,7 +487,8 @@ namespace BotSharp.Core.Repository
|
|||
File.WriteAllText(instFile, agent.Instruction);
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
|
||||
ResetLocalAgents();
|
||||
}
|
||||
|
||||
public void BulkInsertUserAgents(List<UserAgent> userAgents)
|
||||
|
|
@ -509,7 +521,7 @@ namespace BotSharp.Core.Repository
|
|||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
Reset();
|
||||
ResetLocalAgents();
|
||||
}
|
||||
|
||||
public bool DeleteAgents()
|
||||
|
|
@ -526,7 +538,7 @@ namespace BotSharp.Core.Repository
|
|||
var agentDir = GetAgentDataDir(agentId);
|
||||
if (string.IsNullOrEmpty(agentDir)) return false;
|
||||
|
||||
// Delete agent user relationships
|
||||
// Delete user agents
|
||||
var usersDir = Path.Combine(_dbSettings.FileRepository, USERS_FOLDER);
|
||||
if (Directory.Exists(usersDir))
|
||||
{
|
||||
|
|
@ -544,9 +556,27 @@ namespace BotSharp.Core.Repository
|
|||
}
|
||||
}
|
||||
|
||||
// Delete role agents
|
||||
var rolesDir = Path.Combine(_dbSettings.FileRepository, ROLES_FOLDER);
|
||||
if (Directory.Exists(rolesDir))
|
||||
{
|
||||
foreach (var roleDir in Directory.GetDirectories(rolesDir))
|
||||
{
|
||||
var roleAgentFile = Directory.GetFiles(roleDir).FirstOrDefault(x => Path.GetFileName(x) == ROLE_AGENT_FILE);
|
||||
if (string.IsNullOrEmpty(roleAgentFile)) continue;
|
||||
|
||||
var text = File.ReadAllText(roleAgentFile);
|
||||
var roleAgents = JsonSerializer.Deserialize<List<RoleAgent>>(text, _options);
|
||||
if (roleAgents.IsNullOrEmpty()) continue;
|
||||
|
||||
roleAgents = roleAgents?.Where(x => x.AgentId != agentId)?.ToList() ?? [];
|
||||
File.WriteAllText(roleAgentFile, JsonSerializer.Serialize(roleAgents, _options));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete agent folder
|
||||
Directory.Delete(agentDir, true);
|
||||
Reset();
|
||||
ResetLocalAgents();
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
|
|
@ -555,10 +585,11 @@ namespace BotSharp.Core.Repository
|
|||
}
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
private void ResetLocalAgents()
|
||||
{
|
||||
_agents = [];
|
||||
_userAgents = [];
|
||||
_roleAgents = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ public partial class FileRepository
|
|||
#region Task
|
||||
public PagedItems<AgentTask> GetAgentTasks(AgentTaskFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = AgentTaskFilter.Empty();
|
||||
}
|
||||
|
||||
var tasks = new List<AgentTask>();
|
||||
var pager = filter.Pager ?? new Pagination();
|
||||
var skipCount = 0;
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ namespace BotSharp.Core.Repository
|
|||
|
||||
Directory.Delete(convDir, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -322,6 +323,11 @@ namespace BotSharp.Core.Repository
|
|||
|
||||
public PagedItems<Conversation> GetConversations(ConversationFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = ConversationFilter.Empty();
|
||||
}
|
||||
|
||||
var records = new List<Conversation>();
|
||||
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
|
||||
var pager = filter?.Pager ?? new Pagination();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Repository;
|
||||
|
||||
public partial class FileRepository
|
||||
{
|
||||
public bool RefreshRoles(IEnumerable<Role> roles)
|
||||
{
|
||||
if (roles.IsNullOrEmpty()) return false;
|
||||
|
||||
var validRoles = roles.Where(x => !string.IsNullOrWhiteSpace(x.Id)
|
||||
&& !string.IsNullOrWhiteSpace(x.Name)).ToList();
|
||||
if (validRoles.IsNullOrEmpty()) return false;
|
||||
|
||||
var baseDir = Path.Combine(_dbSettings.FileRepository, ROLES_FOLDER);
|
||||
if (Directory.Exists(baseDir))
|
||||
{
|
||||
Directory.Delete(baseDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(baseDir);
|
||||
|
||||
foreach (var role in validRoles)
|
||||
{
|
||||
var dir = Path.Combine(baseDir, role.Id);
|
||||
Directory.CreateDirectory(dir);
|
||||
Thread.Sleep(50);
|
||||
var roleFile = Path.Combine(dir, ROLE_FILE);
|
||||
role.CreatedTime = DateTime.UtcNow;
|
||||
role.UpdatedTime = DateTime.UtcNow;
|
||||
File.WriteAllText(roleFile, JsonSerializer.Serialize(role, _options));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public IEnumerable<Role> GetRoles(RoleFilter filter)
|
||||
{
|
||||
var roles = Roles;
|
||||
if (filter == null)
|
||||
{
|
||||
filter = RoleFilter.Empty();
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
if (!filter.Names.IsNullOrEmpty())
|
||||
{
|
||||
roles = roles.Where(x => filter.Names.Contains(x.Name));
|
||||
}
|
||||
|
||||
if (!filter.ExcludeRoles.IsNullOrEmpty())
|
||||
{
|
||||
roles = roles.Where(x => !filter.ExcludeRoles.Contains(x.Name));
|
||||
}
|
||||
|
||||
return roles.ToList();
|
||||
}
|
||||
|
||||
public Role? GetRoleDetails(string roleId, bool includeAgent = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roleId)) return null;
|
||||
|
||||
var role = Roles.FirstOrDefault(x => x.Id == roleId);
|
||||
if (role == null) return null;
|
||||
|
||||
var agentActions = new List<RoleAgentAction>();
|
||||
var roleAgents = RoleAgents?.Where(x => x.RoleId == roleId)?.ToList() ?? [];
|
||||
|
||||
if (!includeAgent)
|
||||
{
|
||||
agentActions = roleAgents.Select(x => new RoleAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
}).ToList();
|
||||
role.AgentActions = agentActions;
|
||||
return role;
|
||||
}
|
||||
|
||||
var agentIds = roleAgents.Select(x => x.AgentId).Distinct().ToList();
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
|
||||
foreach (var item in roleAgents)
|
||||
{
|
||||
var found = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
if (found == null) continue;
|
||||
|
||||
agentActions.Add(new RoleAgentAction
|
||||
{
|
||||
Id = item.Id,
|
||||
AgentId = found.Id,
|
||||
Agent = found,
|
||||
Actions = item.Actions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
role.AgentActions = agentActions;
|
||||
return role;
|
||||
}
|
||||
|
||||
public bool UpdateRole(Role role, bool updateRoleAgents = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(role?.Id) || string.IsNullOrEmpty(role?.Name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dir = Path.Combine(_dbSettings.FileRepository, ROLES_FOLDER, role.Id);
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
|
||||
var roleFile = Path.Combine(dir, ROLE_FILE);
|
||||
role.CreatedTime = DateTime.UtcNow;
|
||||
role.UpdatedTime = DateTime.UtcNow;
|
||||
File.WriteAllText(roleFile, JsonSerializer.Serialize(role, _options));
|
||||
|
||||
if (updateRoleAgents)
|
||||
{
|
||||
var roleAgents = role.AgentActions?.Select(x => new RoleAgent
|
||||
{
|
||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||
RoleId = role.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions ?? [],
|
||||
CreatedTime = DateTime.UtcNow,
|
||||
UpdatedTime = DateTime.UtcNow
|
||||
})?.ToList() ?? [];
|
||||
|
||||
var roleAgentFile = Path.Combine(dir, ROLE_AGENT_FILE);
|
||||
File.WriteAllText(roleAgentFile, JsonSerializer.Serialize(roleAgents, _options));
|
||||
_roleAgents = [];
|
||||
}
|
||||
|
||||
_roles = [];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace BotSharp.Core.Repository;
|
||||
|
|
@ -73,6 +71,11 @@ public partial class FileRepository
|
|||
|
||||
public PagedItems<User> GetUsers(UserFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = UserFilter.Empty();
|
||||
}
|
||||
|
||||
var users = Users;
|
||||
|
||||
// Apply filters
|
||||
|
|
@ -92,39 +95,15 @@ public partial class FileRepository
|
|||
{
|
||||
users = users.Where(x => filter.Roles.Contains(x.Role));
|
||||
}
|
||||
if (!filter.Types.IsNullOrEmpty())
|
||||
{
|
||||
users = users.Where(x => filter.Types.Contains(x.Type));
|
||||
}
|
||||
if (!filter.Sources.IsNullOrEmpty())
|
||||
{
|
||||
users = users.Where(x => filter.Sources.Contains(x.Source));
|
||||
}
|
||||
|
||||
// Get user agents
|
||||
var userIds = users.Select(x => x.Id).ToList();
|
||||
var userAgents = UserAgents.Where(x => userIds.Contains(x.UserId)).ToList();
|
||||
var agentIds = userAgents?.Select(x => x.AgentId)?.Distinct()?.ToList() ?? [];
|
||||
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
foreach (var item in userAgents)
|
||||
{
|
||||
item.Agent = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
}
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
var found = userAgents.Where(x => x.UserId == user.Id).ToList();
|
||||
if (found.IsNullOrEmpty()) continue;
|
||||
|
||||
user.AgentActions = found.Select(x => new UserAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Agent = x.Agent,
|
||||
Actions = x.Actions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new PagedItems<User>
|
||||
{
|
||||
Items = users.OrderByDescending(x => x.CreatedTime).Skip(filter.Offset).Take(filter.Size),
|
||||
|
|
@ -132,7 +111,53 @@ public partial class FileRepository
|
|||
};
|
||||
}
|
||||
|
||||
public bool UpdateUser(User user, bool isUpdateUserAgents = false)
|
||||
public User? GetUserDetails(string userId, bool includeAgent = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId)) return null;
|
||||
|
||||
var user = Users.FirstOrDefault(x => x.Id == userId || x.ExternalId == userId);
|
||||
if (user == null) return null;
|
||||
|
||||
var agentActions = new List<UserAgentAction>();
|
||||
var userAgents = UserAgents?.Where(x => x.UserId == userId)?.ToList() ?? [];
|
||||
|
||||
if (!includeAgent)
|
||||
{
|
||||
agentActions = userAgents.Select(x => new UserAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
}).ToList();
|
||||
user.AgentActions = agentActions;
|
||||
return user;
|
||||
}
|
||||
|
||||
var agentIds = userAgents.Select(x => x.AgentId)?.Distinct().ToList();
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
|
||||
foreach (var item in userAgents)
|
||||
{
|
||||
var found = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
if (found == null) continue;
|
||||
|
||||
agentActions.Add(new UserAgentAction
|
||||
{
|
||||
Id = item.Id,
|
||||
AgentId = found.Id,
|
||||
Agent = found,
|
||||
Actions = item.Actions ?? []
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
user.AgentActions = agentActions;
|
||||
return user;
|
||||
}
|
||||
|
||||
public bool UpdateUser(User user, bool updateUserAgents = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(user?.Id)) return false;
|
||||
|
||||
|
|
@ -146,7 +171,7 @@ public partial class FileRepository
|
|||
user.UpdatedTime = DateTime.UtcNow;
|
||||
File.WriteAllText(userFile, JsonSerializer.Serialize(user, _options));
|
||||
|
||||
if (isUpdateUserAgents)
|
||||
if (updateUserAgents)
|
||||
{
|
||||
var userAgents = user.AgentActions?.Select(x => new UserAgent
|
||||
{
|
||||
|
|
@ -160,10 +185,10 @@ public partial class FileRepository
|
|||
|
||||
var userAgentFile = Path.Combine(dir, USER_AGENT_FILE);
|
||||
File.WriteAllText(userAgentFile, JsonSerializer.Serialize(userAgents, _options));
|
||||
_userAgents = [];
|
||||
}
|
||||
|
||||
_users = [];
|
||||
_userAgents = [];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,30 +22,38 @@ public partial class FileRepository : IBotSharpRepository
|
|||
private const string AGENT_FILE = "agent.json";
|
||||
private const string AGENT_INSTRUCTION_FILE = "instruction";
|
||||
private const string AGENT_SAMPLES_FILE = "samples.txt";
|
||||
private const string USER_FILE = "user.json";
|
||||
private const string USER_AGENT_FILE = "agents.json";
|
||||
private const string CONVERSATION_FILE = "conversation.json";
|
||||
private const string STATS_FILE = "stats.json";
|
||||
private const string DIALOG_FILE = "dialogs.json";
|
||||
private const string STATE_FILE = "state.json";
|
||||
private const string BREAKPOINT_FILE = "breakpoint.json";
|
||||
private const string EXECUTION_LOG_FILE = "execution.log";
|
||||
private const string PLUGIN_CONFIG_FILE = "config.json";
|
||||
private const string AGENT_TASK_PREFIX = "#metadata";
|
||||
private const string AGENT_TASK_SUFFIX = "/metadata";
|
||||
private const string TRANSLATION_MEMORY_FILE = "memory.json";
|
||||
private const string AGENT_INSTRUCTIONS_FOLDER = "instructions";
|
||||
private const string AGENT_FUNCTIONS_FOLDER = "functions";
|
||||
private const string AGENT_TEMPLATES_FOLDER = "templates";
|
||||
private const string AGENT_RESPONSES_FOLDER = "responses";
|
||||
private const string AGENT_TASKS_FOLDER = "tasks";
|
||||
private const string AGENT_TASK_PREFIX = "#metadata";
|
||||
private const string AGENT_TASK_SUFFIX = "/metadata";
|
||||
|
||||
private const string CONVERSATION_FILE = "conversation.json";
|
||||
private const string DIALOG_FILE = "dialogs.json";
|
||||
private const string STATE_FILE = "state.json";
|
||||
private const string BREAKPOINT_FILE = "breakpoint.json";
|
||||
private const string TRANSLATION_MEMORY_FILE = "memory.json";
|
||||
|
||||
private const string USERS_FOLDER = "users";
|
||||
private const string USER_FILE = "user.json";
|
||||
private const string USER_AGENT_FILE = "agents.json";
|
||||
|
||||
private const string ROLES_FOLDER = "roles";
|
||||
private const string ROLE_FILE = "role.json";
|
||||
private const string ROLE_AGENT_FILE = "agents.json";
|
||||
|
||||
private const string KNOWLEDGE_FOLDER = "knowledgebase";
|
||||
private const string VECTOR_FOLDER = "vector";
|
||||
private const string COLLECTION_CONFIG_FILE = "collection-config.json";
|
||||
private const string KNOWLEDGE_DOC_FOLDER = "document";
|
||||
private const string KNOWLEDGE_DOC_META_FILE = "meta.json";
|
||||
|
||||
private const string EXECUTION_LOG_FILE = "execution.log";
|
||||
private const string PLUGIN_CONFIG_FILE = "config.json";
|
||||
private const string STATS_FILE = "stats.json";
|
||||
|
||||
public FileRepository(
|
||||
IServiceProvider services,
|
||||
BotSharpDatabaseSettings dbSettings,
|
||||
|
|
@ -73,12 +81,68 @@ public partial class FileRepository : IBotSharpRepository
|
|||
_dbSettings.FileRepository = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, _dbSettings.FileRepository);
|
||||
}
|
||||
|
||||
private List<Role> _roles = new List<Role>();
|
||||
private List<User> _users = new List<User>();
|
||||
private List<Agent> _agents = new List<Agent>();
|
||||
private List<RoleAgent> _roleAgents = new List<RoleAgent>();
|
||||
private List<UserAgent> _userAgents = new List<UserAgent>();
|
||||
private List<Conversation> _conversations = new List<Conversation>();
|
||||
private PluginConfig? _pluginConfig = null;
|
||||
|
||||
private IQueryable<Role> Roles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_roles.IsNullOrEmpty())
|
||||
{
|
||||
return _roles.AsQueryable();
|
||||
}
|
||||
|
||||
var dir = Path.Combine(_dbSettings.FileRepository, ROLES_FOLDER);
|
||||
_roles = new List<Role>();
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
foreach (var d in Directory.GetDirectories(dir))
|
||||
{
|
||||
var roleFile = Path.Combine(d, ROLE_FILE);
|
||||
if (!Directory.Exists(d) || !File.Exists(roleFile))
|
||||
continue;
|
||||
|
||||
var json = File.ReadAllText(roleFile);
|
||||
_roles.Add(JsonSerializer.Deserialize<Role>(json, _options));
|
||||
}
|
||||
}
|
||||
return _roles.AsQueryable();
|
||||
}
|
||||
}
|
||||
|
||||
private IQueryable<RoleAgent> RoleAgents
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_roleAgents.IsNullOrEmpty())
|
||||
{
|
||||
return _roleAgents.AsQueryable();
|
||||
}
|
||||
|
||||
var dir = Path.Combine(_dbSettings.FileRepository, ROLES_FOLDER);
|
||||
_roleAgents = new List<RoleAgent>();
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
foreach (var d in Directory.GetDirectories(dir))
|
||||
{
|
||||
var file = Path.Combine(d, ROLE_AGENT_FILE);
|
||||
if (!Directory.Exists(d) || !File.Exists(file))
|
||||
continue;
|
||||
|
||||
var json = File.ReadAllText(file);
|
||||
_roleAgents.AddRange(JsonSerializer.Deserialize<List<RoleAgent>>(json, _options));
|
||||
}
|
||||
}
|
||||
return _roleAgents.AsQueryable();
|
||||
}
|
||||
}
|
||||
|
||||
private IQueryable<User> Users
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
using BotSharp.Abstraction.Users.Enums;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BotSharp.Core.Roles.Services;
|
||||
|
||||
public class RoleService : IRoleService
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly ILogger<RoleService> _logger;
|
||||
|
||||
public RoleService(
|
||||
IServiceProvider services,
|
||||
ILogger<RoleService> logger)
|
||||
{
|
||||
_services = services;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshRoles()
|
||||
{
|
||||
var allRoles = await GetRoleOptions();
|
||||
var roles = allRoles.Select(x => new Role { Id = Guid.NewGuid().ToString(), Name = x }).ToList();
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
return db.RefreshRoles(roles);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetRoleOptions()
|
||||
{
|
||||
var fields = typeof(UserRole).GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(x => x.IsLiteral && !x.IsInitOnly).ToList();
|
||||
|
||||
return fields.Select(x => x.GetValue(null)?.ToString())
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Role>> GetRoles(RoleFilter filter)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var roles = db.GetRoles(filter);
|
||||
return roles;
|
||||
}
|
||||
|
||||
public async Task<Role?> GetRoleDetails(string roleId, bool includeAgent = false)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var role = db.GetRoleDetails(roleId, includeAgent);
|
||||
return role;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRole(Role role, bool isUpdateRoleAgents = false)
|
||||
{
|
||||
if (role == null) return false;
|
||||
|
||||
if (string.IsNullOrEmpty(role.Id))
|
||||
{
|
||||
role.Id = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
return db.UpdateRole(role, isUpdateRoleAgents);
|
||||
}
|
||||
}
|
||||
|
|
@ -407,10 +407,63 @@ public class UserService : IUserService
|
|||
return users;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateUser(User model, bool isUpdateUserAgents = false)
|
||||
public async Task<bool> IsAdminUser(string userId)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
return db.UpdateUser(model, isUpdateUserAgents);
|
||||
var user = db.GetUserById(userId);
|
||||
return user != null && UserConstant.AdminRoles.Contains(user.Role);
|
||||
}
|
||||
|
||||
public async Task<UserAuthorization> GetUserAuthorizations(IEnumerable<string>? agentIds = null)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
var user = db.GetUserById(_user.Id);
|
||||
var auth = new UserAuthorization();
|
||||
|
||||
if (user == null) return auth;
|
||||
|
||||
auth.IsAdmin = UserConstant.AdminRoles.Contains(user.Role);
|
||||
|
||||
var role = db.GetRoles(new RoleFilter { Names = [user.Role] }).FirstOrDefault();
|
||||
var permissions = user.Permissions?.Any() == true ? user.Permissions : role?.Permissions ?? [];
|
||||
auth.Permissions = permissions;
|
||||
|
||||
if (agentIds == null || !agentIds.Any())
|
||||
{
|
||||
return auth;
|
||||
}
|
||||
|
||||
var userAgents = db.GetUserDetails(user.Id)?.AgentActions?
|
||||
.Where(x => agentIds.Contains(x.AgentId) && x.Actions.Any())?.Select(x => new UserAgent
|
||||
{
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
}).ToList() ?? [];
|
||||
|
||||
var userAgentIds = userAgents.Select(x => x.AgentId).ToList();
|
||||
var roleAgents = db.GetRoleDetails(role?.Id)?.AgentActions?
|
||||
.Where(x => !userAgentIds.Contains(x.AgentId))?.Select(x => new UserAgent
|
||||
{
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
})?.ToList() ?? [];
|
||||
|
||||
auth.AgentActions = userAgents.Concat(roleAgents);
|
||||
return auth;
|
||||
}
|
||||
|
||||
public async Task<User?> GetUserDetails(string userId, bool includeAgent = false)
|
||||
{
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
return db.GetUserDetails(userId, includeAgent);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateUser(User user, bool isUpdateUserAgents = false)
|
||||
{
|
||||
if (user == null) return false;
|
||||
|
||||
var db = _services.GetRequiredService<IBotSharpRepository>();
|
||||
return db.UpdateUser(user, isUpdateUserAgents);
|
||||
}
|
||||
|
||||
public async Task<Token> ActiveUser(UserActivationModel model)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ global using BotSharp.Abstraction.Agents;
|
|||
global using BotSharp.Abstraction.Conversations;
|
||||
global using BotSharp.Abstraction.Knowledges;
|
||||
global using BotSharp.Abstraction.Users;
|
||||
global using BotSharp.Abstraction.Roles;
|
||||
global using BotSharp.Abstraction.Roles.Models;
|
||||
global using BotSharp.Abstraction.Utilities;
|
||||
global using BotSharp.Abstraction.Conversations.Models;
|
||||
global using BotSharp.Abstraction.Agents.Settings;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
|
@ -58,20 +57,13 @@ public class AgentController : ControllerBase
|
|||
rule.RedirectToAgentName = found.Name;
|
||||
}
|
||||
|
||||
var editable = true;
|
||||
var chatable = true;
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
if (!UserConstant.AdminRoles.Contains(user?.Role))
|
||||
{
|
||||
var userAgents = await _agentService.GetUserAgents(user?.Id);
|
||||
var actions = userAgents?.FirstOrDefault(x => x.AgentId == targetAgent.Id)?.Actions ?? [];
|
||||
editable = actions.Contains(UserAction.Edit);
|
||||
chatable = actions.Contains(UserAction.Chat);
|
||||
}
|
||||
var auth = await userService.GetUserAuthorizations(new List<string> { targetAgent.Id });
|
||||
|
||||
targetAgent.Editable = editable;
|
||||
targetAgent.Chatable = chatable;
|
||||
targetAgent.Editable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Edit);
|
||||
targetAgent.Chatable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Chat);
|
||||
targetAgent.Trainable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Train);
|
||||
targetAgent.Evaluable = auth.IsAgentActionAllowed(targetAgent.Id, UserAction.Evaluate);
|
||||
return targetAgent;
|
||||
}
|
||||
|
||||
|
|
@ -94,27 +86,14 @@ public class AgentController : ControllerBase
|
|||
};
|
||||
}
|
||||
|
||||
var userAgents = new List<UserAgent>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
if (!UserConstant.AdminRoles.Contains(user.Role))
|
||||
{
|
||||
userAgents = await _agentService.GetUserAgents(user.Id);
|
||||
}
|
||||
|
||||
var auth = await userService.GetUserAuthorizations(pagedAgents.Items.Select(x => x.Id));
|
||||
agents = pagedAgents?.Items?.Select(x =>
|
||||
{
|
||||
var chatable = true;
|
||||
var editable = true;
|
||||
if (!UserConstant.AdminRoles.Contains(user.Role))
|
||||
{
|
||||
var actions = userAgents.FirstOrDefault(a => a.AgentId == x.Id)?.Actions ?? [];
|
||||
chatable = actions.Contains(UserAction.Chat);
|
||||
editable = actions.Contains(UserAction.Edit);
|
||||
}
|
||||
|
||||
var model = AgentViewModel.FromAgent(x);
|
||||
model.Editable = editable;
|
||||
model.Chatable = chatable;
|
||||
model.Editable = auth.IsAgentActionAllowed(x.Id, UserAction.Edit);
|
||||
model.Chatable = auth.IsAgentActionAllowed(x.Id, UserAction.Chat);
|
||||
model.Trainable = auth.IsAgentActionAllowed(x.Id, UserAction.Train);
|
||||
model.Evaluable = auth.IsAgentActionAllowed(x.Id, UserAction.Evaluate);
|
||||
return model;
|
||||
})?.ToList() ?? [];
|
||||
|
||||
|
|
|
|||
|
|
@ -120,12 +120,13 @@ public class KnowledgeBaseController : ControllerBase
|
|||
[HttpPost("/knowledge/document/{collection}/upload")]
|
||||
public async Task<UploadKnowledgeResponse> UploadKnowledgeDocuments([FromRoute] string collection, [FromBody] VectorKnowledgeUploadRequest request)
|
||||
{
|
||||
var response = await _knowledgeService.UploadDocumentsToKnowledge(collection, request.Files);
|
||||
var response = await _knowledgeService.UploadDocumentsToKnowledge(collection, request.Files, request.ChunkOption);
|
||||
return response;
|
||||
}
|
||||
|
||||
[HttpPost("/knowledge/document/{collection}/form-upload")]
|
||||
public async Task<UploadKnowledgeResponse> UploadKnowledgeDocuments([FromRoute] string collection, [FromForm] IEnumerable<IFormFile> files)
|
||||
public async Task<UploadKnowledgeResponse> UploadKnowledgeDocuments([FromRoute] string collection,
|
||||
[FromForm] IEnumerable<IFormFile> files, [FromForm] ChunkOption? option = null)
|
||||
{
|
||||
if (files.IsNullOrEmpty())
|
||||
{
|
||||
|
|
@ -143,7 +144,7 @@ public class KnowledgeBaseController : ControllerBase
|
|||
});
|
||||
}
|
||||
|
||||
var response = await _knowledgeService.UploadDocumentsToKnowledge(collection, docs);
|
||||
var response = await _knowledgeService.UploadDocumentsToKnowledge(collection, docs, option);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ public class PluginController : ControllerBase
|
|||
[HttpGet("/plugins")]
|
||||
public async Task<PagedItems<PluginDef>> GetPlugins([FromQuery] PluginFilter filter)
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
if (!UserConstant.AdminRoles.Contains(user?.Role))
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return new PagedItems<PluginDef>();
|
||||
}
|
||||
|
|
@ -55,7 +54,11 @@ public class PluginController : ControllerBase
|
|||
{
|
||||
Roles = new List<string> { UserRole.Root, UserRole.Admin }
|
||||
},
|
||||
new PluginMenuDef("Users", link: "page/users", icon: "bx bx-user", weight: 33)
|
||||
new PluginMenuDef("Roles", link: "page/roles", icon: "bx bx-group", weight: 33)
|
||||
{
|
||||
Roles = new List<string> { UserRole.Root, UserRole.Admin }
|
||||
},
|
||||
new PluginMenuDef("Users", link: "page/users", icon: "bx bx-user", weight: 34)
|
||||
{
|
||||
Roles = new List<string> { UserRole.Root, UserRole.Admin }
|
||||
}
|
||||
|
|
@ -91,4 +94,10 @@ public class PluginController : ControllerBase
|
|||
var loader = _services.GetRequiredService<PluginLoader>();
|
||||
return loader.UpdatePluginStatus(_services, id, false);
|
||||
}
|
||||
|
||||
private async Task<bool> IsValidUser()
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
return await userService.IsAdminUser(_user.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
using BotSharp.Abstraction.Roles;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
|
||||
namespace BotSharp.OpenAPI.Controllers;
|
||||
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
public class RoleController : ControllerBase
|
||||
{
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly IRoleService _roleService;
|
||||
private readonly IUserIdentity _user;
|
||||
|
||||
public RoleController(
|
||||
IServiceProvider services,
|
||||
IRoleService roleService,
|
||||
IUserIdentity user)
|
||||
{
|
||||
_services = services;
|
||||
_roleService = roleService;
|
||||
_user = user;
|
||||
}
|
||||
|
||||
[HttpPost("/role/refresh")]
|
||||
public async Task<bool> RefreshRoles()
|
||||
{
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await _roleService.RefreshRoles();
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("/role/options")]
|
||||
public async Task<IEnumerable<string>> GetRoleOptions()
|
||||
{
|
||||
return await _roleService.GetRoleOptions();
|
||||
}
|
||||
|
||||
[HttpPost("/roles")]
|
||||
public async Task<IEnumerable<RoleViewModel>> GetRoles([FromBody] RoleFilter? filter = null)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = RoleFilter.Empty();
|
||||
}
|
||||
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return Enumerable.Empty<RoleViewModel>();
|
||||
}
|
||||
|
||||
var roles = await _roleService.GetRoles(filter);
|
||||
return roles.Select(x => RoleViewModel.FromRole(x)).ToList();
|
||||
}
|
||||
|
||||
[HttpGet("/role/{id}/details")]
|
||||
public async Task<RoleViewModel> GetRoleDetails([FromRoute] string id)
|
||||
{
|
||||
var role = await _roleService.GetRoleDetails(id, includeAgent: true);
|
||||
return RoleViewModel.FromRole(role);
|
||||
}
|
||||
|
||||
[HttpPut("/role")]
|
||||
public async Task<bool> UpdateRole([FromBody] RoleUpdateModel model)
|
||||
{
|
||||
if (model == null) return false;
|
||||
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var role = RoleUpdateModel.ToRole(model);
|
||||
return await _roleService.UpdateRole(role, isUpdateRoleAgents: true);
|
||||
}
|
||||
|
||||
private async Task<bool> IsValidUser()
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
return await userService.IsAdminUser(_user.Id);
|
||||
}
|
||||
}
|
||||
|
|
@ -182,8 +182,8 @@ public class UserController : ControllerBase
|
|||
public async Task<PagedItems<UserViewModel>> GetUsers([FromBody] UserFilter filter)
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
if (user == null || !UserConstant.AdminRoles.Contains(user.Role))
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return new PagedItems<UserViewModel>();
|
||||
}
|
||||
|
|
@ -198,19 +198,26 @@ public class UserController : ControllerBase
|
|||
};
|
||||
}
|
||||
|
||||
[HttpGet("/user/{id}/details")]
|
||||
public async Task<UserViewModel> GetUserDetails(string id)
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUserDetails(id, includeAgent: true);
|
||||
return UserViewModel.FromUser(user);
|
||||
}
|
||||
|
||||
[HttpPut("/user")]
|
||||
public async Task<bool> UpdateUser([FromBody] UserUpdateModel model)
|
||||
{
|
||||
if (model == null) return false;
|
||||
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var user = await userService.GetUser(_user.Id);
|
||||
if (user == null || !UserConstant.AdminRoles.Contains(user.Role))
|
||||
var isValid = await IsValidUser();
|
||||
if (!isValid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
var updated = await userService.UpdateUser(UserUpdateModel.ToUser(model), isUpdateUserAgents: true);
|
||||
return updated;
|
||||
}
|
||||
|
|
@ -245,6 +252,12 @@ public class UserController : ControllerBase
|
|||
|
||||
|
||||
#region Private methods
|
||||
private async Task<bool> IsValidUser()
|
||||
{
|
||||
var userService = _services.GetRequiredService<IUserService>();
|
||||
return await userService.IsAdminUser(_user.Id);
|
||||
}
|
||||
|
||||
private FileContentResult BuildFileResult(string file)
|
||||
{
|
||||
var fileStorage = _services.GetRequiredService<IFileStorageService>();
|
||||
|
|
|
|||
|
|
@ -32,3 +32,4 @@ global using BotSharp.OpenAPI.ViewModels.Conversations;
|
|||
global using BotSharp.OpenAPI.ViewModels.Users;
|
||||
global using BotSharp.OpenAPI.ViewModels.Agents;
|
||||
global using BotSharp.OpenAPI.ViewModels.Files;
|
||||
global using BotSharp.OpenAPI.ViewModels.Roles;
|
||||
|
|
@ -48,6 +48,8 @@ public class AgentViewModel
|
|||
|
||||
public bool Editable { get; set; }
|
||||
public bool Chatable { get; set; }
|
||||
public bool Trainable { get; set; }
|
||||
public bool Evaluable { get; set; }
|
||||
|
||||
[JsonPropertyName("created_datetime")]
|
||||
public DateTime CreatedDateTime { get; set; }
|
||||
|
|
|
|||
|
|
@ -6,4 +6,7 @@ public class VectorKnowledgeUploadRequest
|
|||
{
|
||||
[JsonPropertyName("files")]
|
||||
public IEnumerable<ExternalFileModel> Files { get; set; } = new List<ExternalFileModel>();
|
||||
|
||||
[JsonPropertyName("chunk_option")]
|
||||
public ChunkOption? ChunkOption { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Roles.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Roles;
|
||||
|
||||
public class RoleAgentActionViewModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("agent_id")]
|
||||
public string AgentId { get; set; }
|
||||
|
||||
[JsonPropertyName("agent")]
|
||||
public Agent? Agent { get; set; }
|
||||
|
||||
[JsonPropertyName("actions")]
|
||||
public IEnumerable<string> Actions { get; set; } = [];
|
||||
|
||||
|
||||
public static RoleAgentActionViewModel ToViewModel(RoleAgentAction action)
|
||||
{
|
||||
return new RoleAgentActionViewModel
|
||||
{
|
||||
Id = action.Id,
|
||||
AgentId = action.AgentId,
|
||||
Agent = action.Agent,
|
||||
Actions = action.Actions
|
||||
};
|
||||
}
|
||||
|
||||
public static RoleAgentAction ToDomainModel(RoleAgentActionViewModel action)
|
||||
{
|
||||
return new RoleAgentAction
|
||||
{
|
||||
Id = action.Id,
|
||||
AgentId = action.AgentId,
|
||||
Actions = action.Actions
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using BotSharp.Abstraction.Roles.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Roles;
|
||||
|
||||
public class RoleUpdateModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("permissions")]
|
||||
public IEnumerable<string> Permissions { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("agent_actions")]
|
||||
public IEnumerable<RoleAgentActionViewModel> AgentActions { get; set; } = [];
|
||||
|
||||
public static Role ToRole(RoleUpdateModel model)
|
||||
{
|
||||
return new Role
|
||||
{
|
||||
Id = model.Id,
|
||||
Name = model.Name,
|
||||
Permissions = model.Permissions,
|
||||
AgentActions = model.AgentActions?.Select(x => RoleAgentActionViewModel.ToDomainModel(x)) ?? []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
using BotSharp.Abstraction.Roles.Models;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace BotSharp.OpenAPI.ViewModels.Roles;
|
||||
|
||||
public class RoleViewModel
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public string? Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("permissions")]
|
||||
public IEnumerable<string> Permissions { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("agent_actions")]
|
||||
public IEnumerable<RoleAgentActionViewModel> AgentActions { get; set; } = [];
|
||||
|
||||
[JsonPropertyName("create_date")]
|
||||
public DateTime? CreateDate { get; set; }
|
||||
|
||||
[JsonPropertyName("update_date")]
|
||||
public DateTime? UpdateDate { get; set; }
|
||||
|
||||
public static RoleViewModel FromRole(Role? role)
|
||||
{
|
||||
if (role == null) return null;
|
||||
|
||||
return new RoleViewModel
|
||||
{
|
||||
Id = role.Id,
|
||||
Name = role.Name,
|
||||
Permissions = role.Permissions,
|
||||
AgentActions = role.AgentActions?.Select(x => RoleAgentActionViewModel.ToViewModel(x)) ?? [],
|
||||
CreateDate = role.CreatedTime != default ? role.CreatedTime : null,
|
||||
UpdateDate = role.UpdatedTime != default ? role.UpdatedTime : null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,8 @@ namespace BotSharp.Plugin.KnowledgeBase.Services;
|
|||
|
||||
public partial class KnowledgeService
|
||||
{
|
||||
public async Task<UploadKnowledgeResponse> UploadDocumentsToKnowledge(string collectionName, IEnumerable<ExternalFileModel> files)
|
||||
public async Task<UploadKnowledgeResponse> UploadDocumentsToKnowledge(string collectionName,
|
||||
IEnumerable<ExternalFileModel> files, ChunkOption? option = null)
|
||||
{
|
||||
var res = new UploadKnowledgeResponse
|
||||
{
|
||||
|
|
@ -48,7 +49,7 @@ public partial class KnowledgeService
|
|||
{
|
||||
// Get document info
|
||||
var (contentType, bytes) = await GetFileInfo(file);
|
||||
var contents = await GetFileContent(contentType, bytes);
|
||||
var contents = await GetFileContent(contentType, bytes, option ?? ChunkOption.Default());
|
||||
|
||||
// Save document
|
||||
var fileId = Guid.NewGuid();
|
||||
|
|
@ -369,13 +370,13 @@ public partial class KnowledgeService
|
|||
}
|
||||
|
||||
#region Read doc content
|
||||
private async Task<IEnumerable<string>> GetFileContent(string contentType, byte[] bytes)
|
||||
private async Task<IEnumerable<string>> GetFileContent(string contentType, byte[] bytes, ChunkOption option)
|
||||
{
|
||||
IEnumerable<string> results = new List<string>();
|
||||
|
||||
if (contentType.IsEqualTo(MediaTypeNames.Text.Plain))
|
||||
{
|
||||
results = await ReadTxt(bytes);
|
||||
results = await ReadTxt(bytes, option);
|
||||
}
|
||||
else if (contentType.IsEqualTo(MediaTypeNames.Application.Pdf))
|
||||
{
|
||||
|
|
@ -385,7 +386,7 @@ public partial class KnowledgeService
|
|||
return results;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<string>> ReadTxt(byte[] bytes)
|
||||
private async Task<IEnumerable<string>> ReadTxt(byte[] bytes, ChunkOption option)
|
||||
{
|
||||
using var stream = new MemoryStream(bytes);
|
||||
using var reader = new StreamReader(stream);
|
||||
|
|
@ -393,12 +394,7 @@ public partial class KnowledgeService
|
|||
reader.Close();
|
||||
stream.Close();
|
||||
|
||||
var lines = TextChopper.Chop(content, new ChunkOption
|
||||
{
|
||||
Size = 1024,
|
||||
Conjunction = 12,
|
||||
SplitByWord = true,
|
||||
});
|
||||
var lines = TextChopper.Chop(content, option);
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>$(LangVersion)</LangVersion>
|
||||
<VersionPrefix>$(BotSharpVersion)</VersionPrefix>
|
||||
<GeneratePackageOnBuild>$(GeneratePackageOnBuild)</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>$(GenerateDocumentationFile)</GenerateDocumentationFile>
|
||||
<OutputPath>$(SolutionDir)packages</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LangChain.Providers.Google.VertexAI" Version="0.15.3-dev.58" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Infrastructure\BotSharp.Abstraction\BotSharp.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Loggers;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using LangChain.Providers;
|
||||
using LangChain.Providers.Google.VertexAI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.Plugin.VertexAI.Providers
|
||||
{
|
||||
public class ChatCompletionProvider(VertexAIConfiguration config,
|
||||
ChatSettings settings,
|
||||
ILogger<TextCompletionProvider> logger,
|
||||
IServiceProvider services) : IChatCompletion
|
||||
{
|
||||
public string Provider => "vertexai";
|
||||
private readonly VertexAIConfiguration _config = config;
|
||||
private readonly ChatSettings? _settings = settings;
|
||||
private readonly IServiceProvider _services = services;
|
||||
private readonly ILogger _logger = logger;
|
||||
public required string _model;
|
||||
|
||||
public void SetModelName(string model)
|
||||
{
|
||||
_model = model;
|
||||
}
|
||||
|
||||
public async Task<RoleDialogModel> GetChatCompletions(Agent agent, List<RoleDialogModel> conversations)
|
||||
{
|
||||
var hooks = _services.GetServices<IContentGeneratingHook>().ToList();
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.BeforeGenerating(agent, conversations)).ToArray());
|
||||
var client = new VertexAIProvider(_config);
|
||||
var model = new VertexAIChatModel(client, _model);
|
||||
var messages = conversations
|
||||
.Select(c => new Message(c.Content, c.Role == AgentRole.User ? MessageRole.Human : MessageRole.Ai)).ToList();
|
||||
|
||||
var response = await model.GenerateAsync(new ChatRequest { Messages = messages }, _settings);
|
||||
|
||||
var msg = new RoleDialogModel(MessageRole.Ai.ToString(), response.LastMessageContent)
|
||||
{
|
||||
CurrentAgentId = agent.Id
|
||||
};
|
||||
|
||||
Task.WaitAll(hooks.Select(hook =>
|
||||
hook.AfterGenerated(msg, new TokenStatsModel
|
||||
{
|
||||
Prompt = response.Messages[0].Content,
|
||||
Model = _model
|
||||
})).ToArray());
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public Task<bool> GetChatCompletionsAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived, Func<RoleDialogModel, Task> onFunctionExecuting)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> GetChatCompletionsStreamingAsync(Agent agent, List<RoleDialogModel> conversations, Func<RoleDialogModel, Task> onMessageReceived)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using BotSharp.Abstraction.Agents.Enums;
|
||||
using BotSharp.Abstraction.Agents.Models;
|
||||
using BotSharp.Abstraction.Conversations.Models;
|
||||
using BotSharp.Abstraction.Loggers;
|
||||
using BotSharp.Abstraction.MLTasks;
|
||||
using LangChain.Providers;
|
||||
using LangChain.Providers.Google.VertexAI;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BotSharp.Plugin.VertexAI.Providers
|
||||
{
|
||||
public class TextCompletionProvider(VertexAIConfiguration config,
|
||||
ChatSettings settings,
|
||||
ILogger<TextCompletionProvider> logger,
|
||||
IServiceProvider services) : ITextCompletion
|
||||
{
|
||||
public string Provider => "vertexai";
|
||||
private readonly VertexAIConfiguration _config = config;
|
||||
private readonly ChatSettings? _settings = settings;
|
||||
private readonly IServiceProvider _services = services;
|
||||
private readonly ILogger _logger = logger;
|
||||
public required string _model;
|
||||
|
||||
public async Task<string> GetCompletion(string text, string agentId, string messageId)
|
||||
{
|
||||
var contentHooks = _services.GetServices<IContentGeneratingHook>().ToList();
|
||||
var agent = new Agent()
|
||||
{
|
||||
Id = agentId,
|
||||
};
|
||||
|
||||
var client = new VertexAIProvider(_config);
|
||||
var model = new VertexAIChatModel(client, _model);
|
||||
var response = await model.GenerateAsync(text, _settings);
|
||||
|
||||
var responseMessage = new RoleDialogModel(AgentRole.Assistant, response.LastMessageContent)
|
||||
{
|
||||
CurrentAgentId = agentId,
|
||||
MessageId = messageId
|
||||
};
|
||||
|
||||
Task.WaitAll(contentHooks.Select(hook =>
|
||||
hook.AfterGenerated(responseMessage, new TokenStatsModel
|
||||
{
|
||||
Prompt = text,
|
||||
Provider = Provider,
|
||||
Model = _model,
|
||||
PromptCount = response.Usage.TotalTokens,
|
||||
CompletionCount = response.Usage.OutputTokens
|
||||
})).ToArray());
|
||||
|
||||
return response.LastMessageContent;
|
||||
}
|
||||
|
||||
public void SetModelName(string model)
|
||||
{
|
||||
_model = model;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/Plugins/BotSharp.Plugin.LangChain/VertexAiPlugin.cs
Normal file
28
src/Plugins/BotSharp.Plugin.LangChain/VertexAiPlugin.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using BotSharp.Abstraction.MLTasks;
|
||||
using BotSharp.Abstraction.Plugins;
|
||||
using BotSharp.Abstraction.Settings;
|
||||
using BotSharp.Plugin.VertexAI.Providers;
|
||||
using LangChain.Providers.Google.VertexAI;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BotSharp.Plugin.VertexAI;
|
||||
|
||||
public class VertexAiPlugin : IBotSharpPlugin
|
||||
{
|
||||
public string Id => "962ff441-2b40-4db4-b530-49efb1688a75";
|
||||
public string Name => "VertexAI";
|
||||
public string Description => "VertexAI Service including text generation, text to image and other AI services.";
|
||||
public string IconUrl => "https://upload.wikimedia.org/wikipedia/commons/thumb/0/05/Vertex_AI_Logo.svg/480px-Vertex_AI_Logo.svg.png";
|
||||
|
||||
public void RegisterDI(IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddScoped(provider =>
|
||||
{
|
||||
var settingService = provider.GetRequiredService<ISettingService>();
|
||||
return settingService.Bind<VertexAIConfiguration>("VertexAI");
|
||||
});
|
||||
services.AddScoped<IChatCompletion, ChatCompletionProvider>();
|
||||
services.AddScoped<ITextCompletion, TextCompletionProvider>();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Collections;
|
||||
|
||||
public class RoleAgentDocument : MongoBase
|
||||
{
|
||||
public string RoleId { get; set; }
|
||||
public string AgentId { get; set; }
|
||||
public IEnumerable<string> Actions { get; set; } = [];
|
||||
public DateTime CreatedTime { get; set; }
|
||||
public DateTime UpdatedTime { get; set; }
|
||||
|
||||
public RoleAgent ToRoleAgent()
|
||||
{
|
||||
return new RoleAgent
|
||||
{
|
||||
Id = Id,
|
||||
RoleId = RoleId,
|
||||
AgentId = AgentId,
|
||||
Actions = Actions,
|
||||
CreatedTime = CreatedTime,
|
||||
UpdatedTime = UpdatedTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Collections;
|
||||
|
||||
public class RoleDocument : MongoBase
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public IEnumerable<string> Permissions { get; set; } = [];
|
||||
public DateTime CreatedTime { get; set; }
|
||||
public DateTime UpdatedTime { get; set; }
|
||||
|
||||
|
||||
public Role ToRole()
|
||||
{
|
||||
return new Role
|
||||
{
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
Permissions = Permissions,
|
||||
CreatedTime = CreatedTime,
|
||||
UpdatedTime = UpdatedTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -159,4 +159,10 @@ public class MongoDbContext
|
|||
|
||||
public IMongoCollection<KnowledgeCollectionFileMetaDocument> KnowledgeCollectionFileMeta
|
||||
=> Database.GetCollection<KnowledgeCollectionFileMetaDocument>($"{_collectionPrefix}_KnowledgeCollectionFileMeta");
|
||||
|
||||
public IMongoCollection<RoleDocument> Roles
|
||||
=> Database.GetCollection<RoleDocument>($"{_collectionPrefix}_Roles");
|
||||
|
||||
public IMongoCollection<RoleAgentDocument> RoleAgents
|
||||
=> Database.GetCollection<RoleAgentDocument>($"{_collectionPrefix}_RoleAgents");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using BotSharp.Abstraction.Agents.Models;
|
|||
using BotSharp.Abstraction.Functions.Models;
|
||||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Routing.Models;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
||||
|
|
@ -283,6 +284,11 @@ public partial class MongoRepository
|
|||
|
||||
public List<Agent> GetAgents(AgentFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = AgentFilter.Empty();
|
||||
}
|
||||
|
||||
var agents = new List<Agent>();
|
||||
var builder = Builders<AgentDocument>.Filter;
|
||||
var filters = new List<FilterDefinition<AgentDocument>>() { builder.Empty };
|
||||
|
|
@ -292,6 +298,11 @@ public partial class MongoRepository
|
|||
filters.Add(builder.Eq(x => x.Name, filter.AgentName));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(filter.SimilarName))
|
||||
{
|
||||
filters.Add(builder.Regex(x => x.Name, new BsonRegularExpression(filter.SimilarName, "i")));
|
||||
}
|
||||
|
||||
if (filter.Disabled.HasValue)
|
||||
{
|
||||
filters.Add(builder.Eq(x => x.Disabled, filter.Disabled.Value));
|
||||
|
|
@ -327,8 +338,6 @@ public partial class MongoRepository
|
|||
|
||||
if (found.IsNullOrEmpty()) return [];
|
||||
|
||||
var agentIds = found.Select(x => x.AgentId).Distinct().ToList();
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
var res = found.Select(x => new UserAgent
|
||||
{
|
||||
Id = x.Id,
|
||||
|
|
@ -339,6 +348,8 @@ public partial class MongoRepository
|
|||
UpdatedTime = x.UpdatedTime
|
||||
}).ToList();
|
||||
|
||||
var agentIds = found.Select(x => x.AgentId).Distinct().ToList();
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
foreach (var item in res)
|
||||
{
|
||||
var agent = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
|
|
@ -450,6 +461,7 @@ public partial class MongoRepository
|
|||
try
|
||||
{
|
||||
_dc.UserAgents.DeleteMany(Builders<UserAgentDocument>.Filter.Empty);
|
||||
_dc.RoleAgents.DeleteMany(Builders<RoleAgentDocument>.Filter.Empty);
|
||||
_dc.Agents.DeleteMany(Builders<AgentDocument>.Filter.Empty);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -467,10 +479,12 @@ public partial class MongoRepository
|
|||
|
||||
var agentFilter = Builders<AgentDocument>.Filter.Eq(x => x.Id, agentId);
|
||||
var userAgentFilter = Builders<UserAgentDocument>.Filter.Eq(x => x.AgentId, agentId);
|
||||
var roleAgentFilter = Builders<RoleAgentDocument>.Filter.Eq(x => x.AgentId, agentId);
|
||||
var agentTaskFilter = Builders<AgentTaskDocument>.Filter.Eq(x => x.AgentId, agentId);
|
||||
|
||||
_dc.Agents.DeleteOne(agentFilter);
|
||||
_dc.UserAgents.DeleteMany(userAgentFilter);
|
||||
_dc.RoleAgents.DeleteMany(roleAgentFilter);
|
||||
_dc.AgentTasks.DeleteMany(agentTaskFilter);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ public partial class MongoRepository
|
|||
#region Task
|
||||
public PagedItems<AgentTask> GetAgentTasks(AgentTaskFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = AgentTaskFilter.Empty();
|
||||
}
|
||||
|
||||
var pager = filter.Pager ?? new Pagination();
|
||||
var builder = Builders<AgentTaskDocument>.Filter;
|
||||
var filters = new List<FilterDefinition<AgentTaskDocument>>() { builder.Empty };
|
||||
|
|
|
|||
|
|
@ -281,6 +281,11 @@ public partial class MongoRepository
|
|||
|
||||
public PagedItems<Conversation> GetConversations(ConversationFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = ConversationFilter.Empty();
|
||||
}
|
||||
|
||||
var convBuilder = Builders<ConversationDocument>.Filter;
|
||||
var convFilters = new List<FilterDefinition<ConversationDocument>>() { convBuilder.Empty };
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Roles.Models;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
||||
public partial class MongoRepository
|
||||
{
|
||||
public bool RefreshRoles(IEnumerable<Role> roles)
|
||||
{
|
||||
if (roles.IsNullOrEmpty()) return false;
|
||||
|
||||
var validRoles = roles.Where(x => !string.IsNullOrWhiteSpace(x.Id)
|
||||
&& !string.IsNullOrWhiteSpace(x.Name)).ToList();
|
||||
if (validRoles.IsNullOrEmpty()) return false;
|
||||
|
||||
|
||||
// Clear data
|
||||
_dc.RoleAgents.DeleteMany(Builders<RoleAgentDocument>.Filter.Empty);
|
||||
_dc.Roles.DeleteMany(Builders<RoleDocument>.Filter.Empty);
|
||||
|
||||
var roleDocs = validRoles.Select(x => new RoleDocument
|
||||
{
|
||||
Id = x.Id,
|
||||
Name = x.Name,
|
||||
Permissions = x.Permissions,
|
||||
CreatedTime = DateTime.UtcNow,
|
||||
UpdatedTime = DateTime.UtcNow
|
||||
});
|
||||
_dc.Roles.InsertMany(roleDocs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public IEnumerable<Role> GetRoles(RoleFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = RoleFilter.Empty();
|
||||
}
|
||||
|
||||
var roleBuilder = Builders<RoleDocument>.Filter;
|
||||
var roleFilters = new List<FilterDefinition<RoleDocument>>() { roleBuilder.Empty };
|
||||
|
||||
// Apply filters
|
||||
if (!filter.Names.IsNullOrEmpty())
|
||||
{
|
||||
roleFilters.Add(roleBuilder.In(x => x.Name, filter.Names));
|
||||
}
|
||||
|
||||
if (!filter.ExcludeRoles.IsNullOrEmpty())
|
||||
{
|
||||
roleFilters.Add(roleBuilder.Nin(x => x.Name, filter.ExcludeRoles));
|
||||
}
|
||||
|
||||
// Search
|
||||
var roleDocs = _dc.Roles.Find(roleBuilder.And(roleFilters)).ToList();
|
||||
var roles = roleDocs.Select(x => x.ToRole()).ToList();
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
public Role? GetRoleDetails(string roleId, bool includeAgent = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(roleId)) return null;
|
||||
|
||||
var roleDoc = _dc.Roles.Find(Builders<RoleDocument>.Filter.Eq(x => x.Id, roleId)).FirstOrDefault();
|
||||
if (roleDoc == null) return null;
|
||||
|
||||
var agentActions = new List<RoleAgentAction>();
|
||||
var role = roleDoc.ToRole();
|
||||
var roleAgentDocs = _dc.RoleAgents.Find(Builders<RoleAgentDocument>.Filter.Eq(x => x.RoleId, roleId)).ToList();
|
||||
|
||||
if (!includeAgent)
|
||||
{
|
||||
agentActions = roleAgentDocs.Select(x => new RoleAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
}).ToList();
|
||||
role.AgentActions = agentActions;
|
||||
return role;
|
||||
}
|
||||
|
||||
var agentIds = roleAgentDocs.Select(x => x.AgentId).Distinct().ToList();
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
|
||||
foreach (var item in roleAgentDocs)
|
||||
{
|
||||
var found = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
if (found == null) continue;
|
||||
|
||||
agentActions.Add(new RoleAgentAction
|
||||
{
|
||||
Id = item.Id,
|
||||
AgentId = found.Id,
|
||||
Agent = found,
|
||||
Actions = item.Actions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
role.AgentActions = agentActions;
|
||||
return role;
|
||||
}
|
||||
|
||||
public bool UpdateRole(Role role, bool updateRoleAgents = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(role?.Id)) return false;
|
||||
|
||||
var roleFilter = Builders<RoleDocument>.Filter.Eq(x => x.Id, role.Id);
|
||||
var roleUpdate = Builders<RoleDocument>.Update
|
||||
.Set(x => x.Name, role.Name)
|
||||
.Set(x => x.Permissions, role.Permissions)
|
||||
.Set(x => x.CreatedTime, DateTime.UtcNow)
|
||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.Roles.UpdateOne(roleFilter, roleUpdate, _options);
|
||||
|
||||
if (updateRoleAgents)
|
||||
{
|
||||
var roleAgentDocs = role.AgentActions?.Select(x => new RoleAgentDocument
|
||||
{
|
||||
Id = !string.IsNullOrEmpty(x.Id) ? x.Id : Guid.NewGuid().ToString(),
|
||||
RoleId = role.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions,
|
||||
CreatedTime = DateTime.UtcNow,
|
||||
UpdatedTime = DateTime.UtcNow
|
||||
})?.ToList() ?? [];
|
||||
|
||||
var toDelete = _dc.RoleAgents.Find(Builders<RoleAgentDocument>.Filter.And(
|
||||
Builders<RoleAgentDocument>.Filter.Eq(x => x.RoleId, role.Id),
|
||||
Builders<RoleAgentDocument>.Filter.Nin(x => x.Id, roleAgentDocs.Select(x => x.Id))
|
||||
)).ToList();
|
||||
|
||||
_dc.RoleAgents.DeleteMany(Builders<RoleAgentDocument>.Filter.In(x => x.Id, toDelete.Select(x => x.Id)));
|
||||
foreach (var doc in roleAgentDocs)
|
||||
{
|
||||
var roleAgentFilter = Builders<RoleAgentDocument>.Filter.Eq(x => x.Id, doc.Id);
|
||||
var roleAgentUpdate = Builders<RoleAgentDocument>.Update
|
||||
.Set(x => x.Id, doc.Id)
|
||||
.Set(x => x.RoleId, role.Id)
|
||||
.Set(x => x.AgentId, doc.AgentId)
|
||||
.Set(x => x.Actions, doc.Actions)
|
||||
.Set(x => x.UpdatedTime, DateTime.UtcNow);
|
||||
|
||||
_dc.RoleAgents.UpdateOne(roleAgentFilter, roleAgentUpdate, _options);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ using BotSharp.Abstraction.Agents.Models;
|
|||
using BotSharp.Abstraction.Repositories.Filters;
|
||||
using BotSharp.Abstraction.Users.Enums;
|
||||
using BotSharp.Abstraction.Users.Models;
|
||||
using MongoDB.Driver;
|
||||
using System.Globalization;
|
||||
|
||||
namespace BotSharp.Plugin.MongoStorage.Repository;
|
||||
|
||||
|
|
@ -15,7 +17,9 @@ public partial class MongoRepository
|
|||
|
||||
public User? GetUserByPhone(string phone, string role = null, string regionCode = "CN")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(phone))
|
||||
string phoneSecond = string.Empty;
|
||||
// if phone number length is less than 4, return null
|
||||
if (string.IsNullOrWhiteSpace(phone) || phone?.Length < 4)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -36,22 +40,19 @@ public partial class MongoRepository
|
|||
|
||||
public User? GetUserById(string id)
|
||||
{
|
||||
var user = _dc.Users.AsQueryable()
|
||||
.FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id));
|
||||
var user = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == id || (x.ExternalId != null && x.ExternalId == id));
|
||||
return user != null ? user.ToUser() : null;
|
||||
}
|
||||
|
||||
public List<User> GetUserByIds(List<string> ids)
|
||||
{
|
||||
var users = _dc.Users.AsQueryable()
|
||||
.Where(x => ids.Contains(x.Id) || (x.ExternalId != null && ids.Contains(x.ExternalId))).ToList();
|
||||
var users = _dc.Users.AsQueryable().Where(x => ids.Contains(x.Id) || (x.ExternalId != null && ids.Contains(x.ExternalId))).ToList();
|
||||
return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List<User>();
|
||||
}
|
||||
|
||||
public List<User> GetUsersByAffiliateId(string affiliateId)
|
||||
{
|
||||
var users = _dc.Users.AsQueryable()
|
||||
.Where(x => x.AffiliateId == affiliateId).ToList();
|
||||
var users = _dc.Users.AsQueryable().Where(x => x.AffiliateId == affiliateId).ToList();
|
||||
return users?.Any() == true ? users.Select(x => x.ToUser()).ToList() : new List<User>();
|
||||
}
|
||||
|
||||
|
|
@ -168,6 +169,11 @@ public partial class MongoRepository
|
|||
|
||||
public PagedItems<User> GetUsers(UserFilter filter)
|
||||
{
|
||||
if (filter == null)
|
||||
{
|
||||
filter = UserFilter.Empty();
|
||||
}
|
||||
|
||||
var userBuilder = Builders<UserDocument>.Filter;
|
||||
var userFilters = new List<FilterDefinition<UserDocument>>() { userBuilder.Empty };
|
||||
|
||||
|
|
@ -188,6 +194,10 @@ public partial class MongoRepository
|
|||
{
|
||||
userFilters.Add(userBuilder.In(x => x.Role, filter.Roles));
|
||||
}
|
||||
if (!filter.Types.IsNullOrEmpty())
|
||||
{
|
||||
userFilters.Add(userBuilder.In(x => x.Type, filter.Types));
|
||||
}
|
||||
if (!filter.Sources.IsNullOrEmpty())
|
||||
{
|
||||
userFilters.Add(userBuilder.In(x => x.Source, filter.Sources));
|
||||
|
|
@ -202,44 +212,6 @@ public partial class MongoRepository
|
|||
var count = _dc.Users.CountDocuments(filterDef);
|
||||
|
||||
var users = userDocs.Select(x => x.ToUser()).ToList();
|
||||
var userIds = users.Select(x => x.Id).ToList();
|
||||
var userAgents = _dc.UserAgents.AsQueryable().Where(x => userIds.Contains(x.UserId)).Select(x => new UserAgent
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions ?? Enumerable.Empty<string>(),
|
||||
CreatedTime = x.CreatedTime,
|
||||
UpdatedTime = x.UpdatedTime
|
||||
}).ToList();
|
||||
var agentIds = userAgents.Select(x => x.AgentId).Distinct().ToList();
|
||||
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
foreach (var item in userAgents)
|
||||
{
|
||||
var agent = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
if (agent == null) continue;
|
||||
|
||||
item.Agent = agent;
|
||||
}
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
var found = userAgents.Where(x => x.UserId == user.Id).ToList();
|
||||
if (found.IsNullOrEmpty()) continue;
|
||||
|
||||
user.AgentActions = found.Select(x => new UserAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Agent = x.Agent,
|
||||
Actions = x.Actions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return new PagedItems<User>
|
||||
{
|
||||
Items = users,
|
||||
|
|
@ -247,8 +219,60 @@ public partial class MongoRepository
|
|||
};
|
||||
}
|
||||
|
||||
public User? GetUserDetails(string userId, bool includeAgent = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(userId)) return null;
|
||||
|
||||
public bool UpdateUser(User user, bool isUpdateUserAgents = false)
|
||||
var userDoc = _dc.Users.AsQueryable().FirstOrDefault(x => x.Id == userId || x.ExternalId == userId);
|
||||
if (userDoc == null) return null;
|
||||
|
||||
var agentActions = new List<UserAgentAction>();
|
||||
var user = userDoc.ToUser();
|
||||
var userAgents = _dc.UserAgents.AsQueryable().Where(x => x.UserId == userId).Select(x => new UserAgent
|
||||
{
|
||||
Id = x.Id,
|
||||
UserId = x.UserId,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions ?? Enumerable.Empty<string>()
|
||||
}).ToList();
|
||||
|
||||
if (!includeAgent)
|
||||
{
|
||||
agentActions = userAgents.Select(x => new UserAgentAction
|
||||
{
|
||||
Id = x.Id,
|
||||
AgentId = x.AgentId,
|
||||
Actions = x.Actions
|
||||
}).ToList();
|
||||
user.AgentActions = agentActions;
|
||||
return user;
|
||||
}
|
||||
|
||||
var agentIds = userAgents.Select(x => x.AgentId)?.Distinct().ToList();
|
||||
if (!agentIds.IsNullOrEmpty())
|
||||
{
|
||||
var agents = GetAgents(new AgentFilter { AgentIds = agentIds });
|
||||
|
||||
foreach (var item in userAgents)
|
||||
{
|
||||
var found = agents.FirstOrDefault(x => x.Id == item.AgentId);
|
||||
if (found == null) continue;
|
||||
|
||||
agentActions.Add(new UserAgentAction
|
||||
{
|
||||
Id = item.Id,
|
||||
AgentId = found.Id,
|
||||
Agent = found,
|
||||
Actions = item.Actions
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
user.AgentActions = agentActions;
|
||||
return user;
|
||||
}
|
||||
|
||||
public bool UpdateUser(User user, bool updateUserAgents = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(user?.Id)) return false;
|
||||
|
||||
|
|
@ -261,7 +285,7 @@ public partial class MongoRepository
|
|||
|
||||
_dc.Users.UpdateOne(userFilter, userUpdate);
|
||||
|
||||
if (isUpdateUserAgents)
|
||||
if (updateUserAgents)
|
||||
{
|
||||
var userAgentDocs = user.AgentActions?.Select(x => new UserAgentDocument
|
||||
{
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ public class SecondaryStagePlanFn : IFunctionCallback
|
|||
var conv = _services.GetRequiredService<IConversationService>();
|
||||
var wholeDialogs = conv.GetDialogHistory();
|
||||
|
||||
wholeDialogs.Last().Content += "\r\nOutput in JSON format.";
|
||||
|
||||
var completion = CompletionProvider.GetChatCompletion(_services,
|
||||
provider: plannerAgent.LlmConfig.Provider,
|
||||
model: plannerAgent.LlmConfig.Model);
|
||||
|
|
|
|||
|
|
@ -72,8 +72,12 @@ public class SummaryPlanFn : IFunctionCallback
|
|||
message.Content = summary.Content;
|
||||
|
||||
// Validate the sql result
|
||||
await fn.InvokeFunction("validate_sql", message);
|
||||
|
||||
var args = JsonSerializer.Deserialize<SummaryPlan>(message.FunctionArgs);
|
||||
if (args.IsSqlTemplate == false)
|
||||
{
|
||||
await fn.InvokeFunction("validate_sql", message);
|
||||
}
|
||||
|
||||
await HookEmitter.Emit<IPlanningHook>(_services, async hook =>
|
||||
await hook.OnPlanningCompleted(nameof(TwoStageTaskPlanner), message)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
namespace BotSharp.Plugin.Planner.TwoStaging.Models;
|
||||
|
||||
public class SummaryPlan
|
||||
{
|
||||
[JsonPropertyName("is_sql_template")]
|
||||
public bool IsSqlTemplate { get; set; } = false;
|
||||
}
|
||||
|
|
@ -4,6 +4,10 @@
|
|||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"is_sql_template": {
|
||||
"type": "boolean",
|
||||
"description": "If user request is to generate sql template instead of actual sql statement."
|
||||
},
|
||||
"related_tables": {
|
||||
"type": "array",
|
||||
"description": "table name in planning steps",
|
||||
|
|
@ -13,6 +17,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"required": [ "related_tables" ]
|
||||
"required": [ "related_tables", "is_sql_template" ]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
You are a planning summarizer. You will generate the final output in JSON format based on the task description, knowledge and related table structure and relationship.
|
||||
You are a planning summarizer. You will generate the final output in JSON format with short explanation based on the task description, knowledge and related table structure and relationship.
|
||||
|
||||
Requirements:
|
||||
{{ summary_requirements }}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public class ExecuteQueryFn : IFunctionCallback
|
|||
public async Task<bool> Execute(RoleDialogModel message)
|
||||
{
|
||||
var args = JsonSerializer.Deserialize<ExecuteQueryArgs>(message.FunctionArgs);
|
||||
var refinedArgs = await RefineSqlStatement(message, args);
|
||||
//var refinedArgs = await RefineSqlStatement(message, args);
|
||||
var dbHook = _services.GetRequiredService<ISqlDriverHook>();
|
||||
var dbType = dbHook.GetDatabaseType(message);
|
||||
|
||||
|
|
@ -38,13 +38,13 @@ public class ExecuteQueryFn : IFunctionCallback
|
|||
{
|
||||
var results = dbType.ToLower() switch
|
||||
{
|
||||
"mysql" => RunQueryInMySql(refinedArgs.SqlStatements),
|
||||
"sqlserver" => RunQueryInSqlServer(refinedArgs.SqlStatements),
|
||||
"redshift" => RunQueryInRedshift(refinedArgs.SqlStatements),
|
||||
"mysql" => RunQueryInMySql(args.SqlStatements),
|
||||
"sqlserver" => RunQueryInSqlServer(args.SqlStatements),
|
||||
"redshift" => RunQueryInRedshift(args.SqlStatements),
|
||||
_ => throw new NotImplementedException($"Database type {dbType} is not supported.")
|
||||
};
|
||||
|
||||
if (refinedArgs.SqlStatements.Length == 1 && refinedArgs.SqlStatements[0].StartsWith("DROP TABLE"))
|
||||
if (args.SqlStatements.Length == 1 && args.SqlStatements[0].StartsWith("DROP TABLE"))
|
||||
{
|
||||
message.Content = "Drop table successfully";
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class SqlValidateFn : IFunctionCallback
|
|||
var dbType = dbHook.GetDatabaseType(message);
|
||||
var validateSql = dbType.ToLower() switch
|
||||
{
|
||||
"mysql" => $"explain\r\n{sql}",
|
||||
"mysql" => $"explain\r\n{sql.Replace("SET ", "-- SET ", StringComparison.InvariantCultureIgnoreCase).Replace(";", "; explain ").TrimEnd("explain ".ToCharArray())}",
|
||||
"sqlserver" => $"SET PARSEONLY ON;\r\n{sql}\r\nSET PARSEONLY OFF;",
|
||||
"redshift" => $"explain\r\n{sql}",
|
||||
_ => throw new NotImplementedException($"Database type {dbType} is not supported.")
|
||||
|
|
@ -49,7 +49,7 @@ public class SqlValidateFn : IFunctionCallback
|
|||
var msgCopy = RoleDialogModel.From(message);
|
||||
msgCopy.FunctionArgs = JsonSerializer.Serialize(new ExecuteQueryArgs
|
||||
{
|
||||
SqlStatements = new string[] { validateSql }
|
||||
SqlStatements = [validateSql]
|
||||
});
|
||||
|
||||
var fn = _services.GetRequiredService<IRoutingService>();
|
||||
|
|
@ -74,7 +74,7 @@ public class SqlValidateFn : IFunctionCallback
|
|||
Message = "Correct SQL Statement",
|
||||
Data = new Dictionary<string, object>
|
||||
{
|
||||
{ "original_sql", sql },
|
||||
{ "original_sql", message.Content },
|
||||
{ "error_message", ex.Message },
|
||||
{ "table_structure", ddl }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "verify_dictionary_term",
|
||||
"description": "Get id from dictionary table by keyword. Call this function only if need_lookup_dictionary is true and is_insert is false. You can only query one table at a time.",
|
||||
"description": "Get id from dictionary table by keyword. Call this function only if need_lookup_dictionary is true, is_table_from_knowledge is true and is_insert is false. You can only query one table at a time. The table name must come from the global/domain knowledge.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -16,9 +16,13 @@
|
|||
"type": "boolean",
|
||||
"description": "if SQL statement is inserting."
|
||||
},
|
||||
"is_table_from_knowledge": {
|
||||
"type": "boolean",
|
||||
"description": "if table is from the global/domain knowledge."
|
||||
},
|
||||
"tables": {
|
||||
"type": "array",
|
||||
"description": "all related dictionary tables must be from related knowledge in the context",
|
||||
"description": "all related dictionary tables must be from global/domain knowledge in the context",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "table name from related knowledge in the context"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ If not, generate the query step by step based on the planning.
|
|||
|
||||
The query must exactly based on the provided table structure. And carefully review the foreign keys to make sure you include all the accurate information.
|
||||
|
||||
Note: Output should be only the sql query with sql comments that can be directly run in mysql database with version 8.0.
|
||||
Note: Output should be only the sql query with short sql comments and explanation that can be directly run in mysql database with version 8.0.
|
||||
|
||||
Don't use the sql statement that specify target table for update in FROM clause.
|
||||
For example, you CAN'T write query as below:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
You are a sql statement corrector. You will need to refer to the table structure and rewrite the original sql statement so it's using the correct information, e.g. column name.
|
||||
Output the sql statement only without comments, in JSON format: {{ response_format }}
|
||||
Correct the sql statement and keep only the original explanation and comments without any information related to error message{% if response_format %} in JSON format: {{ response_format }} {% endif %}.
|
||||
Make sure all the column names are defined in the Table Structure.
|
||||
|
||||
=====
|
||||
|
|
|
|||
Loading…
Reference in a new issue