Merge pull request #145 from iceljc/features/add-update-agent

Features/add update agent
This commit is contained in:
Haiping 2023-09-15 17:42:55 -05:00 committed by GitHub
commit 8552357878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 765 additions and 362 deletions

View file

@ -0,0 +1,13 @@
namespace BotSharp.Abstraction.Agents.Enums;
public enum AgentField
{
All = 1,
Name,
Description,
IsPublic,
Instruction,
Function,
Template,
Response
}

View file

@ -17,7 +17,7 @@ public interface IAgentService
Task<Agent> GetAgent(string id);
Task<bool> DeleteAgent(string id);
Task UpdateAgent(Agent agent);
Task UpdateAgent(Agent agent, AgentField updateField);
Task UpdateAgentFromFile(string id);
string GetDataDir();
string GetAgentDataDir(string agentId);

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.Routing.Models;
using BotSharp.Abstraction.Users.Models;
@ -15,27 +16,32 @@ public interface IBotSharpRepository
int Transaction<TTableInterface>(Action action);
void Add<TTableInterface>(object entity);
#region User
User GetUserByEmail(string email);
void CreateUser(User user);
void UpdateAgent(Agent agent);
#endregion
#region Agent
void UpdateAgent(Agent agent, AgentField field);
Agent GetAgent(string agentId);
List<string> GetAgentResponses(string agentId, string prefix, string intent);
string GetAgentTemplate(string agentId, string templateName);
#endregion
#region Conversation
void CreateNewConversation(Conversation conversation);
string GetConversationDialog(string conversationId);
void UpdateConversationDialog(string conversationId, string dialogs);
List<StateKeyValue> GetConversationStates(string conversationId);
void UpdateConversationStates(string conversationId, List<StateKeyValue> states);
Conversation GetConversation(string conversationId);
List<Conversation> GetConversations(string userId);
#endregion
#region Routing
List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems);
List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles);
void DeleteRoutingItems();
void DeleteRoutingProfiles();
Agent GetAgent(string agentId);
List<string> GetAgentResponses(string agentId, string prefix, string intent);
void CreateNewConversation(Conversation conversation);
string GetConversationDialog(string conversationId);
void UpdateConversationDialog(string conversationId, string dialogs);
List<StateKeyValue> GetConversationStates(string conversationId);
void UpdateConversationStates(string conversationId, List<StateKeyValue> states);
Conversation GetConversation(string conversationId);
List<Conversation> GetConversations(string userId);
string GetAgentTemplate(string agentId, string templateName);
#endregion
}

View file

@ -6,3 +6,4 @@ global using System.Threading.Tasks;
global using System.ComponentModel.DataAnnotations;
global using BotSharp.Abstraction.Agents.Models;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Enums;

View file

@ -8,11 +8,9 @@ public partial class AgentService
{
public async Task<Agent> CreateAgent(Agent agent)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agentRecord = (from a in db.Agents
join ua in db.UserAgents on a.Id equals ua.AgentId
join u in db.Users on ua.UserId equals u.Id
var agentRecord = (from a in _db.Agents
join ua in _db.UserAgents on a.Id equals ua.AgentId
join u in _db.Users on ua.UserId equals u.Id
where u.ExternalId == _user.Id && a.Name == agent.Name
select a).FirstOrDefault();
@ -43,7 +41,7 @@ public partial class AgentService
.SetResponses(foundAgent.Responses);
}
var user = db.Users.FirstOrDefault(x => x.ExternalId == _user.Id);
var user = _db.Users.FirstOrDefault(x => x.ExternalId == _user.Id);
var userAgentRecord = new UserAgent
{
Id = Guid.NewGuid().ToString(),
@ -53,10 +51,10 @@ public partial class AgentService
UpdatedTime = DateTime.UtcNow
};
db.Transaction<IBotSharpTable>(delegate
_db.Transaction<IBotSharpTable>(delegate
{
db.Add<IBotSharpTable>(agentRecord);
db.Add<IBotSharpTable>(userAgentRecord);
_db.Add<IBotSharpTable>(agentRecord);
_db.Add<IBotSharpTable>(userAgentRecord);
});
return agentRecord;
@ -86,7 +84,7 @@ public partial class AgentService
private string FetchInstructionFromFile(string fileDir)
{
var file = Path.Combine(fileDir, "instruction.liquid");
var file = Path.Combine(fileDir, $"instruction.{_agentSettings.TemplateFormat}");
if (!File.Exists(file)) return null;
var instruction = File.ReadAllText(file);
@ -102,7 +100,7 @@ public partial class AgentService
var splits = fileName.ToLower().Split('.');
var name = splits[0];
var extension = splits[1];
if (name != "instruction" && extension == "liquid")
if (name != "instruction" && extension == _agentSettings.TemplateFormat)
{
var content = File.ReadAllText(file);
templates.Add(new AgentTemplate(name, content));

View file

@ -7,10 +7,9 @@ public partial class AgentService
{
public async Task<List<Agent>> GetAgents()
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var query = from a in db.Agents
join ua in db.UserAgents on a.Id equals ua.AgentId
join u in db.Users on ua.UserId equals u.Id
var query = from a in _db.Agents
join ua in _db.UserAgents on a.Id equals ua.AgentId
join u in _db.Users on ua.UserId equals u.Id
where ua.UserId == _user.Id || u.ExternalId == _user.Id || a.IsPublic
select a;
return query.ToList();
@ -21,8 +20,7 @@ public partial class AgentService
#endif
public async Task<Agent> GetAgent(string id)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var profile = db.GetAgent(id);
var profile = _db.GetAgent(id);
var instructionFile = profile?.Instruction;
if (instructionFile != null)

View file

@ -6,44 +6,38 @@ namespace BotSharp.Core.Agents.Services;
public partial class AgentService
{
public async Task UpdateAgent(Agent agent)
public async Task UpdateAgent(Agent agent, AgentField updateField)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var record = (from a in db.Agents
join ua in db.UserAgents on a.Id equals ua.AgentId
join u in db.Users on ua.UserId equals u.Id
where (ua.UserId == _user.Id || u.ExternalId == _user.Id) &&
a.Id == agent.Id
select a).FirstOrDefault();
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
var record = FindAgent(agent.Id);
if (record == null) return;
record.Name = agent.Name;
record.Name = agent.Name ?? string.Empty;
record.Description = agent.Description ?? string.Empty;
record.Instruction = agent.Instruction ?? string.Empty;
record.Functions = agent.Functions ?? new List<string>();
record.Templates = agent.Templates ?? new List<AgentTemplate>();
record.Responses = agent.Responses ?? new List<AgentResponse>();
if (!string.IsNullOrEmpty(agent.Description))
record.Description = agent.Description;
if (!string.IsNullOrEmpty(agent.Instruction))
record.Instruction = agent.Instruction;
if (!agent.Templates.IsNullOrEmpty())
record.Templates = agent.Templates;
if (!agent.Functions.IsNullOrEmpty())
record.Functions = agent.Functions;
if (!agent.Responses.IsNullOrEmpty())
record.Responses = agent.Responses;
db.UpdateAgent(record);
_db.UpdateAgent(record, updateField);
await Task.CompletedTask;
}
private Agent FindAgent(string agentId)
{
var record = (from a in _db.Agents
join ua in _db.UserAgents on a.Id equals ua.AgentId
join u in _db.Users on ua.UserId equals u.Id
where (ua.UserId == _user.Id || u.ExternalId == _user.Id) &&
a.Id == agentId
select a).FirstOrDefault();
return record;
}
public async Task UpdateAgentFromFile(string id)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var agent = db.Agents?.FirstOrDefault(x => x.Id == id);
var agent = _db.Agents?.FirstOrDefault(x => x.Id == id);
if (agent == null) return;
@ -64,10 +58,9 @@ public partial class AgentService
.SetFunctions(foundAgent.Functions)
.SetResponses(foundAgent.Responses);
db.UpdateAgent(clonedAgent);
_db.UpdateAgent(clonedAgent, AgentField.All);
}
await Task.CompletedTask;
}

View file

@ -1,5 +1,4 @@
using BotSharp.Abstraction.Repositories;
using Microsoft.Extensions.Logging;
using System.IO;
namespace BotSharp.Core.Agents.Services;
@ -7,20 +6,23 @@ namespace BotSharp.Core.Agents.Services;
public partial class AgentService : IAgentService
{
private readonly IServiceProvider _services;
private readonly IBotSharpRepository _db;
private readonly ILogger _logger;
private readonly IUserIdentity _user;
private readonly AgentSettings _settings;
private readonly AgentSettings _agentSettings;
private readonly JsonSerializerOptions _options;
public AgentService(IServiceProvider services,
IBotSharpRepository db,
ILogger<AgentService> logger,
IUserIdentity user,
AgentSettings settings)
AgentSettings agentSettings)
{
_services = services;
_db = db;
_logger = logger;
_user = user;
_settings = settings;
_agentSettings = agentSettings;
_options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
@ -38,7 +40,7 @@ public partial class AgentService : IAgentService
public string GetAgentDataDir(string agentId)
{
var dbSettings = _services.GetRequiredService<BotSharpDatabaseSettings>();
var dir = Path.Combine(dbSettings.FileRepository, _settings.DataDir, agentId);
var dir = Path.Combine(dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);

View file

@ -71,77 +71,55 @@ public class BotSharpDbContext : Database, IBotSharpRepository
}
public void CreateNewConversation(Conversation conversation)
{
throw new NotImplementedException();
}
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
{
throw new NotImplementedException();
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
{
throw new NotImplementedException();
}
public void CreateUser(User user)
{
throw new NotImplementedException();
}
public void DeleteRoutingItems()
{
throw new NotImplementedException();
}
public void DeleteRoutingProfiles()
{
throw new NotImplementedException();
}
#region Agent
public Agent GetAgent(string agentId)
{
throw new NotImplementedException();
}
public void UpdateAgent(Agent agent, AgentField field)
{
throw new NotImplementedException();
}
public string GetAgentTemplate(string agentId, string templateName)
{
throw new NotImplementedException();
}
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
{
throw new NotImplementedException();
}
#endregion
#region Conversation
public void CreateNewConversation(Conversation conversation)
{
throw new NotImplementedException();
}
public Conversation GetConversation(string conversationId)
{
throw new NotImplementedException();
}
public string GetConversationDialog(string conversationId)
{
throw new NotImplementedException();
}
public List<Conversation> GetConversations(string userId)
{
throw new NotImplementedException();
}
public string GetConversationDialog(string conversationId)
{
throw new NotImplementedException();
}
public List<StateKeyValue> GetConversationStates(string conversationId)
{
throw new NotImplementedException();
}
public User GetUserByEmail(string email)
{
throw new NotImplementedException();
}
public void UpdateAgent(Agent agent)
{
throw new NotImplementedException();
}
public void UpdateConversationDialog(string conversationId, string dialogs)
{
throw new NotImplementedException();
@ -151,9 +129,41 @@ public class BotSharpDbContext : Database, IBotSharpRepository
{
throw new NotImplementedException();
}
#endregion
public string GetAgentTemplate(string agentId, string templateName)
#region User
public User GetUserByEmail(string email)
{
throw new NotImplementedException();
}
public void CreateUser(User user)
{
throw new NotImplementedException();
}
#endregion
#region Routing
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
{
throw new NotImplementedException();
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
{
throw new NotImplementedException();
}
public void DeleteRoutingItems()
{
throw new NotImplementedException();
}
public void DeleteRoutingProfiles()
{
throw new NotImplementedException();
}
#endregion
}

View file

@ -4,6 +4,11 @@ using FunctionDef = BotSharp.Abstraction.Functions.Models.FunctionDef;
using BotSharp.Abstraction.Users.Models;
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Routing.Models;
using MongoDB.Driver;
using Microsoft.Extensions.Logging;
using System.Xml.Linq;
using static Tensorflow.TensorShapeProto.Types;
namespace BotSharp.Core.Repository;
public class FileRepository : IBotSharpRepository
@ -66,7 +71,15 @@ public class FileRepository : IBotSharpRepository
foreach (var d in Directory.GetDirectories(dir))
{
var json = File.ReadAllText(Path.Combine(d, "agent.json"));
_agents.Add(JsonSerializer.Deserialize<Agent>(json, _options));
var agent = JsonSerializer.Deserialize<Agent>(json, _options);
if (agent != null)
{
agent = agent.SetInstruction(FetchInstruction(d))
.SetTemplates(FetchTemplates(d))
.SetFunctions(FetchFunctions(d))
.SetResponses(FetchResponses(d));
_agents.Add(agent);
}
}
return _agents.AsQueryable();
}
@ -201,9 +214,7 @@ public class FileRepository : IBotSharpRepository
{
foreach (var conversation in _conversations)
{
var dir = Path.Combine(_dbSettings.FileRepository,
_conversationSettings.DataDir,
conversation.Id);
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
@ -216,9 +227,7 @@ public class FileRepository : IBotSharpRepository
{
foreach (var agent in _agents)
{
var dir = Path.Combine(_dbSettings.FileRepository,
_agentSettings.DataDir,
agent.Id);
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agent.Id);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
@ -231,9 +240,7 @@ public class FileRepository : IBotSharpRepository
{
foreach (var user in _users)
{
var dir = Path.Combine(_dbSettings.FileRepository,
"users",
user.Id);
var dir = Path.Combine(_dbSettings.FileRepository, "users", user.Id);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
@ -262,40 +269,106 @@ public class FileRepository : IBotSharpRepository
return _changedTableNames.Count;
}
public User GetUserByEmail(string email)
#region Agent
public void UpdateAgent(Agent agent, AgentField field)
{
return Users.FirstOrDefault(x => x.Email == email);
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
switch (field)
{
case AgentField.Name:
UpdateAgentName(agent.Id, agent.Name);
break;
case AgentField.Description:
UpdateAgentDescription(agent.Id, agent.Description);
break;
case AgentField.IsPublic:
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
break;
case AgentField.Instruction:
UpdateAgentInstruction(agent.Id, agent.Instruction);
break;
case AgentField.Function:
UpdateAgentFunctions(agent.Id, agent.Functions);
break;
case AgentField.Template:
UpdateAgentTemplates(agent.Id, agent.Templates);
break;
case AgentField.Response:
UpdateAgentResponses(agent.Id, agent.Responses);
break;
case AgentField.All:
UpdateAgentAllFields(agent);
break;
default:
break;
}
}
public void CreateUser(User user)
#region Update Agent Fields
private void UpdateAgentName(string agentId, string name)
{
var userId = Guid.NewGuid().ToString();
var dir = Path.Combine(_dbSettings.FileRepository, "users", userId);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var path = Path.Combine(dir, "user.json");
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
}
if (string.IsNullOrEmpty(name)) return;
public void UpdateAgent(Agent agent)
{
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var dir = GetAgentDataDir(agent.Id);
if (!string.IsNullOrEmpty(agent.Instruction))
{
var instructionFile = Path.Combine(dir, "instruction.liquid");
File.WriteAllText(instructionFile, agent.Instruction);
agent.Name = name;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
if (!agent.Functions.IsNullOrEmpty())
private void UpdateAgentDescription(string agentId, string description)
{
var functionFile = Path.Combine(dir, "functions.json");
if (string.IsNullOrEmpty(description)) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.Description = description;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
private void UpdateAgentIsPublic(string agentId, bool isPublic)
{
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
agent.IsPublic = isPublic;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
}
private void UpdateAgentInstruction(string agentId, string instruction)
{
if (string.IsNullOrEmpty(instruction)) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var instructionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
agentId, $"instruction.{_agentSettings.TemplateFormat}");
File.WriteAllText(instructionFile, instruction);
}
private void UpdateAgentFunctions(string agentId, List<string> inputFunctions)
{
if (inputFunctions.IsNullOrEmpty()) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var functionFile = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir,
agentId, "functions.json");
var functions = new List<string>();
foreach (var function in agent.Functions)
foreach (var function in inputFunctions)
{
var functionDef = JsonSerializer.Deserialize<FunctionDef>(function, _options);
functions.Add(JsonSerializer.Serialize(functionDef, _options));
@ -304,37 +377,81 @@ public class FileRepository : IBotSharpRepository
var functionText = JsonSerializer.Serialize(functions, _options);
File.WriteAllText(functionFile, functionText);
}
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
{
if (templates.IsNullOrEmpty()) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var baseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
foreach (var file in Directory.GetFiles(baseDir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splits = fileName.ToLower().Split('.');
var name = splits[0];
var extension = splits[1];
if (name != "instruction" && extension == _agentSettings.TemplateFormat)
{
File.Delete(file);
}
}
private string GetAgentDataDir(string agentId)
foreach (var template in templates)
{
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
var file = Path.Combine(baseDir, $"{template.Name}.{_agentSettings.TemplateFormat}");
File.WriteAllText(file, template.Content);
}
return dir;
}
public void DeleteRoutingItems()
private void UpdateAgentResponses(string agentId, List<AgentResponse> responses)
{
throw new NotImplementedException();
if (responses.IsNullOrEmpty()) return;
var (agent, agentFile) = GetAgentFromFile(agentId);
if (agent == null) return;
var baseDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
var responseDir = Path.Combine(baseDir, "responses");
if (!Directory.Exists(responseDir))
{
Directory.CreateDirectory(responseDir);
}
public void DeleteRoutingProfiles()
foreach (var file in Directory.GetFiles(responseDir))
{
throw new NotImplementedException();
File.Delete(file);
}
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
for (int i = 0; i < responses.Count; i++)
{
throw new NotImplementedException();
var response = responses[i];
var fileName = $"{response.Prefix}.{response.Intent}.{i}.{_agentSettings.TemplateFormat}";
var file = Path.Combine(responseDir, fileName);
File.WriteAllText(file, response.Content);
}
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
private void UpdateAgentAllFields(Agent inputAgent)
{
throw new NotImplementedException();
var (agent, agentFile) = GetAgentFromFile(inputAgent.Id);
if (agent == null) return;
agent.Name = inputAgent.Name;
agent.Description = inputAgent.Description;
agent.IsPublic = inputAgent.IsPublic;
agent.UpdatedDateTime = DateTime.UtcNow;
var json = JsonSerializer.Serialize(agent, _options);
File.WriteAllText(agentFile, json);
UpdateAgentInstruction(inputAgent.Id, inputAgent.Instruction);
UpdateAgentResponses(inputAgent.Id, inputAgent.Responses);
UpdateAgentTemplates(inputAgent.Id, inputAgent.Templates);
UpdateAgentFunctions(inputAgent.Id, inputAgent.Functions);
}
#endregion
#if !DEBUG
[MemoryCache(10 * 60)]
@ -375,26 +492,29 @@ public class FileRepository : IBotSharpRepository
return null;
}
private string FetchInstruction(string fileDir)
public string GetAgentTemplate(string agentId, string templateName)
{
var file = Path.Combine(fileDir, "instruction.liquid");
if (!File.Exists(file)) return null;
var fileDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(fileDir)) return string.Empty;
var instruction = File.ReadAllText(file);
return instruction;
var lowerTemplateName = templateName?.ToLower();
foreach (var file in Directory.GetFiles(fileDir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splits = fileName.ToLower().Split('.');
var name = splits[0];
var extension = splits[1];
if (name == lowerTemplateName && extension == _agentSettings.TemplateFormat)
{
return File.ReadAllText(file);
}
}
private List<string> FetchFunctions(string fileDir)
{
var file = Path.Combine(fileDir, "functions.json");
if (!File.Exists(file)) return new List<string>();
var functionsJson = File.ReadAllText(file);
var functionDefs = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
var functions = functionDefs.Select(x => JsonSerializer.Serialize(x, _options)).ToList();
return functions;
return string.Empty;
}
#endregion
#region Conversation
public void CreateNewConversation(Conversation conversation)
{
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir, conversation.Id);
@ -473,26 +593,6 @@ public class FileRepository : IBotSharpRepository
return curStates;
}
private string? FindConversationDirectory(string conversationId)
{
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
foreach (var d in Directory.GetDirectories(dir))
{
var path = Path.Combine(d, "conversation.json");
if (!File.Exists(path)) continue;
var json = File.ReadAllText(path);
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
if (conv != null && conv.Id == conversationId)
{
return d;
}
}
return null;
}
public void UpdateConversationStates(string conversationId, List<StateKeyValue> states)
{
var localStates = new List<string>();
@ -559,25 +659,148 @@ public class FileRepository : IBotSharpRepository
return records;
}
#endregion
public string GetAgentTemplate(string agentId, string templateName)
#region User
public User GetUserByEmail(string email)
{
var fileDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(fileDir)) return string.Empty;
return Users.FirstOrDefault(x => x.Email == email);
}
public void CreateUser(User user)
{
var userId = Guid.NewGuid().ToString();
var dir = Path.Combine(_dbSettings.FileRepository, "users", userId);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var path = Path.Combine(dir, "user.json");
File.WriteAllText(path, JsonSerializer.Serialize(user, _options));
}
#endregion
#region Routing
public void DeleteRoutingItems()
{
throw new NotImplementedException();
}
public void DeleteRoutingProfiles()
{
throw new NotImplementedException();
}
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
{
throw new NotImplementedException();
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
{
throw new NotImplementedException();
}
#endregion
#region Private methods
private string GetAgentDataDir(string agentId)
{
var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId);
if (!Directory.Exists(dir))
{
dir = string.Empty;
}
return dir;
}
private (Agent?, string) GetAgentFromFile(string agentId)
{
var dir = GetAgentDataDir(agentId);
var agentFile = Path.Combine(dir, "agent.json");
if (!File.Exists(agentFile)) return (null, string.Empty);
var json = File.ReadAllText(agentFile);
var agent = JsonSerializer.Deserialize<Agent>(json, _options);
return (agent, agentFile);
}
private string FetchInstruction(string fileDir)
{
var file = Path.Combine(fileDir, $"instruction.{_agentSettings.TemplateFormat}");
if (!File.Exists(file)) return string.Empty;
var instruction = File.ReadAllText(file);
return instruction;
}
private List<string> FetchFunctions(string fileDir)
{
var file = Path.Combine(fileDir, "functions.json");
if (!File.Exists(file)) return new List<string>();
var functionsJson = File.ReadAllText(file);
var functionDefs = JsonSerializer.Deserialize<List<FunctionDef>>(functionsJson, _options);
var functions = functionDefs.Select(x => JsonSerializer.Serialize(x, _options)).ToList();
return functions;
}
private List<AgentTemplate> FetchTemplates(string fileDir)
{
var templates = new List<AgentTemplate>();
var lowerTemplateName = templateName?.ToLower();
foreach (var file in Directory.GetFiles(fileDir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splits = fileName.ToLower().Split('.');
var name = splits[0];
var extension = splits[1];
if (name == lowerTemplateName && extension == "liquid")
if (name != "instruction" && extension == _agentSettings.TemplateFormat)
{
return File.ReadAllText(file);
var content = File.ReadAllText(file);
templates.Add(new AgentTemplate(name, content));
}
}
return string.Empty;
return templates;
}
private List<AgentResponse> FetchResponses(string fileDir)
{
var responses = new List<AgentResponse>();
var responseDir = Path.Combine(fileDir, "responses");
if (!Directory.Exists(responseDir)) return responses;
foreach (var file in Directory.GetFiles(responseDir))
{
var fileName = file.Split(Path.DirectorySeparatorChar).Last();
var splits = fileName.ToLower().Split('.');
var prefix = splits[0];
var intent = splits[1];
var content = File.ReadAllText(file);
responses.Add(new AgentResponse(prefix, intent, content));
}
return responses;
}
private string? FindConversationDirectory(string conversationId)
{
var dir = Path.Combine(_dbSettings.FileRepository, _conversationSettings.DataDir);
foreach (var d in Directory.GetDirectories(dir))
{
var path = Path.Combine(d, "conversation.json");
if (!File.Exists(path)) continue;
var json = File.ReadAllText(path);
var conv = JsonSerializer.Deserialize<Conversation>(json, _options);
if (conv != null && conv.Id == conversationId)
{
return d;
}
}
return null;
}
#endregion
}

View file

@ -16,6 +16,7 @@ global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Conversations.Models;
global using BotSharp.Abstraction.Agents.Settings;
global using BotSharp.Abstraction.Conversations.Settings;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Core.Repository;
global using BotSharp.Core.Agents.Services;
global using BotSharp.Core.Conversations.Services;

View file

@ -1,3 +1,4 @@
using BotSharp.Abstraction.Agents.Enums;
using BotSharp.Abstraction.ApiAdapters;
using BotSharp.OpenAPI.ViewModels.Agents;
@ -13,6 +14,13 @@ public class AgentController : ControllerBase, IApiAdapter
_agentService = agentService;
}
[HttpGet("/agents")]
public async Task<List<AgentViewModel>> GetAgents()
{
var agents = await _agentService.GetAgents();
return agents.Select(x => AgentViewModel.FromAgent(x)).ToList();
}
[HttpPost("/agent")]
public async Task<AgentViewModel> CreateAgent(AgentCreationModel agent)
{
@ -20,25 +28,73 @@ public class AgentController : ControllerBase, IApiAdapter
return AgentViewModel.FromAgent(createdAgent);
}
[HttpPut("/agent/{agentId}")]
public async Task UpdateAgent([FromRoute] string agentId,
[FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model);
}
[HttpPut("/agent/file/{agentId}")]
public async Task UpdateAgentFromFile([FromRoute] string agentId)
{
await _agentService.UpdateAgentFromFile(agentId);
}
[HttpGet("/agents")]
public async Task<List<AgentViewModel>> GetAgents()
[HttpPut("/agent/{agentId}/all")]
public async Task UpdateAgent([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var agents = await _agentService.GetAgents();
return agents.Select(x => AgentViewModel.FromAgent(x)).ToList();
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.All);
}
[HttpPut("/agent/{agentId}/name")]
public async Task UpdateAgentName([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Name);
}
[HttpPut("/agent/{agentId}/description")]
public async Task UpdateAgentDescription([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Description);
}
[HttpPut("/agent/{agentId}/is-public")]
public async Task UpdateAgentIsPublic([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.IsPublic);
}
[HttpPut("/agent/{agentId}/instruction")]
public async Task UpdateAgentInstruction([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Instruction);
}
[HttpPut("/agent/{agentId}/functions")]
public async Task UpdateAgentFunctions([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Function);
}
[HttpPut("/agent/{agentId}/templates")]
public async Task UpdateAgenttemplates([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Template);
}
[HttpPut("/agent/{agentId}/responses")]
public async Task UpdateAgentResponses([FromRoute] string agentId, [FromBody] AgentUpdateModel agent)
{
var model = agent.ToAgent();
model.Id = agentId;
await _agentService.UpdateAgent(model, AgentField.Response);
}
}

View file

@ -4,7 +4,7 @@ namespace BotSharp.OpenAPI.ViewModels.Agents;
public class AgentUpdateModel
{
public string Name { get; set; } = string.Empty;
public string? Name { get; set; } = string.Empty;
public string? Description { get; set; }
/// <summary>
@ -34,29 +34,16 @@ public class AgentUpdateModel
public Agent ToAgent()
{
var agent = new Agent
var agent = new Agent()
{
Name = Name
Name = Name ?? string.Empty,
Description = Description ?? string.Empty,
Instruction = Instruction ?? string.Empty,
Templates = Templates ?? new List<AgentTemplate>(),
Functions = Functions ?? new List<string>(),
Responses = Responses ?? new List<AgentResponse>()
};
if (Description != null)
agent.Description = Description;
if (Instruction != null)
agent.Instruction = Instruction;
if (!Templates.IsNullOrEmpty())
agent.Templates = Templates;
if (Samples != null)
agent.Samples = Samples;
if (!Functions.IsNullOrEmpty())
agent.Functions = Functions;
if (!Responses.IsNullOrEmpty())
agent.Responses = Responses;
return agent;
}
}

View file

@ -12,6 +12,7 @@ public class AgentViewModel
public List<string> Functions { get; set; }
public List<AgentResponse> Responses { get; set; }
public bool IsPublic { get; set; }
public DateTime CreatedDateTime { get; set; }
public DateTime UpdatedDateTime { get; set; }
public static AgentViewModel FromAgent(Agent agent)
@ -26,6 +27,7 @@ public class AgentViewModel
Functions = agent.Functions,
Responses = agent.Responses,
IsPublic= agent.IsPublic,
CreatedDateTime = agent.CreatedDateTime,
UpdatedDateTime = agent.UpdatedDateTime
};
}

View file

@ -332,61 +332,127 @@ public class MongoRepository : IBotSharpRepository
return _changedTableNames.Count;
}
public User GetUserByEmail(string email)
{
var user = Users.FirstOrDefault(x => x.Email == email);
return user != null ? new User
{
Id = user.Id.ToString(),
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
Password = user.Password,
Salt = user.Salt,
ExternalId = user.ExternalId,
CreatedTime = user.CreatedTime,
UpdatedTime = user.UpdatedTime
} : null;
}
public void CreateUser(User user)
{
if (user == null) return;
var userCollection = new UserCollection
{
Id = Guid.NewGuid(),
FirstName = user.FirstName,
LastName = user.LastName,
Salt = user.Salt,
Password = user.Password,
Email = user.Email,
ExternalId = user.ExternalId,
CreatedTime = DateTime.UtcNow,
UpdatedTime = DateTime.UtcNow
};
_dc.Users.InsertOne(userCollection);
}
public void UpdateAgent(Agent agent)
#region Agent
public void UpdateAgent(Agent agent, AgentField field)
{
if (agent == null || string.IsNullOrEmpty(agent.Id)) return;
var agentCollection = new AgentCollection
switch (field)
{
Id = Guid.Parse(agent.Id),
Name = agent.Name,
Description = agent.Description,
Instruction = agent.Instruction,
Templates = agent.Templates,
Functions = agent.Functions,
Responses = agent.Responses,
IsPublic = agent.IsPublic,
UpdatedTime = DateTime.UtcNow
};
case AgentField.Name:
UpdateAgentName(agent.Id, agent.Name);
break;
case AgentField.Description:
UpdateAgentDescription(agent.Id, agent.Description);
break;
case AgentField.IsPublic:
UpdateAgentIsPublic(agent.Id, agent.IsPublic);
break;
case AgentField.Instruction:
UpdateAgentInstruction(agent.Id, agent.Instruction);
break;
case AgentField.Function:
UpdateAgentFunctions(agent.Id, agent.Functions);
break;
case AgentField.Template:
UpdateAgentTemplates(agent.Id, agent.Templates);
break;
case AgentField.Response:
UpdateAgentResponses(agent.Id, agent.Responses);
break;
case AgentField.All:
UpdateAgentAllFields(agent);
break;
default:
break;
}
}
#region Update Agent Fields
private void UpdateAgentName(string agentId, string name)
{
if (string.IsNullOrEmpty(name)) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Name, name)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentDescription(string agentId, string description)
{
if (string.IsNullOrEmpty(description)) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Description, description)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentIsPublic(string agentId, bool isPublic)
{
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.IsPublic, isPublic)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentInstruction(string agentId, string instruction)
{
if (string.IsNullOrEmpty(instruction)) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Instruction, instruction)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentFunctions(string agentId, List<string> functions)
{
if (functions.IsNullOrEmpty()) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Functions, functions)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentTemplates(string agentId, List<AgentTemplate> templates)
{
if (templates.IsNullOrEmpty()) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Templates, templates)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentResponses(string agentId, List<AgentResponse> responses)
{
if (responses.IsNullOrEmpty()) return;
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agentId));
var update = Builders<AgentCollection>.Update
.Set(x => x.Responses, responses)
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
private void UpdateAgentAllFields(Agent agent)
{
var filter = Builders<AgentCollection>.Filter.Eq(x => x.Id, Guid.Parse(agent.Id));
var update = Builders<AgentCollection>.Update
.Set(x => x.Name, agent.Name)
@ -396,63 +462,19 @@ public class MongoRepository : IBotSharpRepository
.Set(x => x.Functions, agent.Functions)
.Set(x => x.Responses, agent.Responses)
.Set(x => x.IsPublic, agent.IsPublic)
.Set(x => x.UpdatedTime, agent.UpdatedDateTime);
.Set(x => x.UpdatedTime, DateTime.UtcNow);
_dc.Agents.UpdateOne(filter, update);
}
#endregion
public void DeleteRoutingItems()
{
_dc.RoutingItems.DeleteMany(Builders<RoutingItemCollection>.Filter.Empty);
}
public void DeleteRoutingProfiles()
{
_dc.RoutingProfiles.DeleteMany(Builders<RoutingProfileCollection>.Filter.Empty);
}
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
{
var collections = routingItems?.Select(x => new RoutingItemCollection
{
Id = Guid.NewGuid(),
AgentId = Guid.Parse(x.AgentId),
Name = x.Name,
Description = x.Description,
RequiredFields = x.RequiredFields,
RedirectTo = !string.IsNullOrEmpty(x.RedirectTo) ? Guid.Parse(x.RedirectTo) : null,
Disabled = x.Disabled
})?.ToList() ?? new List<RoutingItemCollection>();
_dc.RoutingItems.InsertMany(collections);
return collections.Select(x => new RoutingItem
public Agent GetAgent(string agentId)
{
Id = x.Id.ToString(),
AgentId = x.AgentId.ToString(),
Name = x.Name,
Description = x.Description,
RequiredFields = x.RequiredFields,
RedirectTo = x.RedirectTo?.ToString(),
Disabled = x.Disabled
}).ToList();
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
{
var collections = profiles?.Select(x => new RoutingProfileCollection
{
Id = Guid.NewGuid(),
Name = x.Name,
AgentIds = x.AgentIds.Select(x => Guid.Parse(x)).ToList()
})?.ToList() ?? new List<RoutingProfileCollection>();
_dc.RoutingProfiles.InsertMany(collections);
return collections.Select(x => new RoutingProfile
{
Id = x.Id.ToString(),
Name = x.Name,
AgentIds = x.AgentIds.Select(x => x.ToString()).ToList()
}).ToList();
var foundAgent = Agents.FirstOrDefault(x => x.Id == agentId);
return foundAgent;
}
public List<string> GetAgentResponses(string agentId, string prefix, string intent)
@ -464,12 +486,16 @@ public class MongoRepository : IBotSharpRepository
return agent.Responses.Where(x => x.Prefix == prefix && x.Intent == intent).Select(x => x.Content).ToList();
}
public Agent GetAgent(string agentId)
public string GetAgentTemplate(string agentId, string templateName)
{
var foundAgent = Agents.FirstOrDefault(x => x.Id == agentId);
return foundAgent;
}
var agent = Agents.FirstOrDefault(x => x.Id == agentId);
if (agent == null) return string.Empty;
return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty;
}
#endregion
#region Conversation
public void CreateNewConversation(Conversation conversation)
{
if (conversation == null) return;
@ -601,12 +627,100 @@ public class MongoRepository : IBotSharpRepository
return records;
}
#endregion
public string GetAgentTemplate(string agentId, string templateName)
#region User
public User GetUserByEmail(string email)
{
var agent = Agents.FirstOrDefault(x => x.Id == agentId);
if (agent == null) return string.Empty;
var user = Users.FirstOrDefault(x => x.Email == email);
return user != null ? new User
{
Id = user.Id.ToString(),
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
Password = user.Password,
Salt = user.Salt,
ExternalId = user.ExternalId,
CreatedTime = user.CreatedTime,
UpdatedTime = user.UpdatedTime
} : null;
}
return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty;
public void CreateUser(User user)
{
if (user == null) return;
var userCollection = new UserCollection
{
Id = Guid.NewGuid(),
FirstName = user.FirstName,
LastName = user.LastName,
Salt = user.Salt,
Password = user.Password,
Email = user.Email,
ExternalId = user.ExternalId,
CreatedTime = DateTime.UtcNow,
UpdatedTime = DateTime.UtcNow
};
_dc.Users.InsertOne(userCollection);
}
#endregion
#region Routing
public void DeleteRoutingItems()
{
_dc.RoutingItems.DeleteMany(Builders<RoutingItemCollection>.Filter.Empty);
}
public void DeleteRoutingProfiles()
{
_dc.RoutingProfiles.DeleteMany(Builders<RoutingProfileCollection>.Filter.Empty);
}
public List<RoutingItem> CreateRoutingItems(List<RoutingItem> routingItems)
{
var collections = routingItems?.Select(x => new RoutingItemCollection
{
Id = Guid.NewGuid(),
AgentId = Guid.Parse(x.AgentId),
Name = x.Name,
Description = x.Description,
RequiredFields = x.RequiredFields,
RedirectTo = !string.IsNullOrEmpty(x.RedirectTo) ? Guid.Parse(x.RedirectTo) : null,
Disabled = x.Disabled
})?.ToList() ?? new List<RoutingItemCollection>();
_dc.RoutingItems.InsertMany(collections);
return collections.Select(x => new RoutingItem
{
Id = x.Id.ToString(),
AgentId = x.AgentId.ToString(),
Name = x.Name,
Description = x.Description,
RequiredFields = x.RequiredFields,
RedirectTo = x.RedirectTo?.ToString(),
Disabled = x.Disabled
}).ToList();
}
public List<RoutingProfile> CreateRoutingProfiles(List<RoutingProfile> profiles)
{
var collections = profiles?.Select(x => new RoutingProfileCollection
{
Id = Guid.NewGuid(),
Name = x.Name,
AgentIds = x.AgentIds.Select(x => Guid.Parse(x)).ToList()
})?.ToList() ?? new List<RoutingProfileCollection>();
_dc.RoutingProfiles.InsertMany(collections);
return collections.Select(x => new RoutingProfile
{
Id = x.Id.ToString(),
Name = x.Name,
AgentIds = x.AgentIds.Select(x => x.ToString()).ToList()
}).ToList();
}
#endregion
}

View file

@ -3,9 +3,8 @@ global using System.Collections.Generic;
global using System.Text;
global using System.Threading.Tasks;
global using System.Linq;
global using System.Text.Json;
global using BotSharp.Abstraction.Repositories;
global using BotSharp.Abstraction.Repositories.Records;
global using BotSharp.Abstraction.Agents.Enums;
global using BotSharp.Abstraction.Utilities;
global using BotSharp.Abstraction.Plugins;
global using Microsoft.Extensions.Configuration;