diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs index bf69bec3..459d8557 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs @@ -1,6 +1,7 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; +using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; namespace BotSharp.Abstraction.Repositories; @@ -34,6 +35,14 @@ public interface IBotSharpRepository string GetAgentTemplate(string agentId, string templateName); #endregion + #region Agent Task + PagedItems GetAgentTasks(AgentTaskFilter filter); + AgentTask? GetAgentTask(string agentId, string taskId); + void InsertAgentTask(AgentTask task); + + bool DeleteAgentTask(string agentId, string taskId); + #endregion + #region Conversation void CreateNewConversation(Conversation conversation); bool DeleteConversation(string conversationId); @@ -49,9 +58,7 @@ public interface IBotSharpRepository List GetLastConversations(); bool TruncateConversation(string conversationId, string messageId); #endregion - #region Statistics - void IncrementConversationCount(); - #endregion + #region Execution Log void AddExecutionLogs(string conversationId, List logs); List GetExecutionLogs(string conversationId); @@ -60,4 +67,8 @@ public interface IBotSharpRepository #region LLM Completion Log void SaveLlmCompletionLog(LlmCompletionLog log); #endregion + + #region Statistics + void IncrementConversationCount(); + #endregion } diff --git a/src/Infrastructure/BotSharp.Abstraction/Tasks/IAgentTaskService.cs b/src/Infrastructure/BotSharp.Abstraction/Tasks/IAgentTaskService.cs index 73a9e272..4ca48a84 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Tasks/IAgentTaskService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Tasks/IAgentTaskService.cs @@ -6,4 +6,10 @@ namespace BotSharp.Abstraction.Tasks; public interface IAgentTaskService { Task> GetTasks(AgentTaskFilter filter); + + Task GetTask(string agentId, string taskId); + + Task CreateTask(AgentTask task); + + Task DeleteTask(string agentId, string taskId); } diff --git a/src/Infrastructure/BotSharp.Abstraction/Tasks/Models/AgentTask.cs b/src/Infrastructure/BotSharp.Abstraction/Tasks/Models/AgentTask.cs index 4505087e..ebbbdf44 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Tasks/Models/AgentTask.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Tasks/Models/AgentTask.cs @@ -10,9 +10,17 @@ public class AgentTask public DateTime CreatedDateTime { get; set; } public DateTime UpdatedDateTime { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] + public string AgentId { get; set; } + [JsonIgnore(Condition = JsonIgnoreCondition.Always)] public Agent Agent { get; set; } + public AgentTask() + { + + } + public AgentTask(string id, string name, string? description = null) { Id = id; diff --git a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs index e2dafa7f..32db29b8 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Utilities/Pagination.cs @@ -13,5 +13,5 @@ public class Pagination public class PagedItems { public int Count { get; set; } - public IEnumerable Items { get; set; } + public IEnumerable Items { get; set; } = new List(); } diff --git a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs index fef3c6dc..0f109358 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/BotSharpDbContext.cs @@ -3,6 +3,7 @@ using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Repositories.Models; +using BotSharp.Abstraction.Tasks.Models; using BotSharp.Abstraction.Users.Models; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -119,6 +120,27 @@ public class BotSharpDbContext : Database, IBotSharpRepository } #endregion + #region Agent Task + public PagedItems GetAgentTasks(AgentTaskFilter filter) + { + throw new NotImplementedException(); + } + + public AgentTask? GetAgentTask(string agentId, string taskId) + { + throw new NotImplementedException(); + } + + public void InsertAgentTask(AgentTask task) + { + throw new NotImplementedException(); + } + + public bool DeleteAgentTask(string agentId, string taskId) + { + throw new NotImplementedException(); + } + #endregion #region Conversation public void CreateNewConversation(Conversation conversation) @@ -184,13 +206,7 @@ public class BotSharpDbContext : Database, IBotSharpRepository throw new NotImplementedException(); } #endregion - #region Stats - public void IncrementConversationCount() - { - throw new NotImplementedException(); - } - #endregion - + #region User public User? GetUserByEmail(string email) => throw new NotImplementedException(); @@ -205,7 +221,6 @@ public class BotSharpDbContext : Database, IBotSharpRepository => throw new NotImplementedException(); #endregion - #region Execution Log public void AddExecutionLogs(string conversationId, List logs) { @@ -224,4 +239,11 @@ public class BotSharpDbContext : Database, IBotSharpRepository throw new NotImplementedException(); } #endregion + + #region Stats + public void IncrementConversationCount() + { + throw new NotImplementedException(); + } + #endregion } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs index 8ea4026b..4bdb3edf 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Agent.cs @@ -1,9 +1,9 @@ using BotSharp.Abstraction.Agents.Models; -using BotSharp.Abstraction.Evaluations.Settings; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; -using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Abstraction.Tasks.Models; +using Microsoft.Extensions.Logging; using System.IO; namespace BotSharp.Core.Repository @@ -326,14 +326,12 @@ namespace BotSharp.Core.Repository var functions = FetchFunctions(dir); var samples = FetchSamples(dir); var templates = FetchTemplates(dir); - var tasks = FetchTasks(dir); var responses = FetchResponses(dir); return record.SetInstruction(instruction) .SetFunctions(functions) .SetTemplates(templates) .SetSamples(samples) - .SetResponses(responses) - .SetTasks(tasks); + .SetResponses(responses); } return null; @@ -407,6 +405,7 @@ namespace BotSharp.Core.Repository return string.Empty; } + public void BulkInsertAgents(List agents) { } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs new file mode 100644 index 00000000..66e60649 --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.AgentTask.cs @@ -0,0 +1,138 @@ +using BotSharp.Abstraction.Repositories.Filters; +using BotSharp.Abstraction.Tasks.Models; +using System.IO; + +namespace BotSharp.Core.Repository; + +public partial class FileRepository +{ + #region Task + public PagedItems GetAgentTasks(AgentTaskFilter filter) + { + var tasks = new List(); + var pager = filter.Pager ?? new Pagination(); + + var dir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir); + if (!Directory.Exists(dir)) return new PagedItems(); + + foreach (var agentDir in Directory.GetDirectories(dir)) + { + var taskDir = Path.Combine(agentDir, "tasks"); + if (!Directory.Exists(taskDir)) continue; + + var agentId = agentDir.Split(Path.DirectorySeparatorChar).Last(); + var matched = true; + if (filter?.AgentId != null) matched = matched && agentId == filter.AgentId; + + if (!matched) continue; + + var agent = ParseAgent(agentDir); + + foreach (var taskFile in Directory.GetFiles(taskDir)) + { + var task = ParseAgentTask(taskFile); + if (task == null) continue; + + task.AgentId = agentId; + task.Agent = agent; + tasks.Add(task); + } + } + + return new PagedItems + { + Items = tasks.Skip(pager.Offset).Take(pager.Size), + Count = tasks.Count + }; + } + + public AgentTask? GetAgentTask(string agentId, string taskId) + { + var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); + if (!Directory.Exists(agentDir)) return null; + + var taskDir = Path.Combine(agentDir, "tasks"); + if (!Directory.Exists(taskDir)) return null; + + var taskFile = Directory.GetFiles(taskDir).FirstOrDefault(file => + { + var fileName = file.Split(Path.DirectorySeparatorChar).Last(); + var id = fileName.Split('.').First(); + return id.IsEqualTo(taskId); + }); + + var task = ParseAgentTask(taskFile); + if (task == null) return null; + + var agent = ParseAgent(agentDir); + task.AgentId = agentId; + task.Agent = agent; + return task; + } + + public void InsertAgentTask(AgentTask task) + { + if (task == null || string.IsNullOrEmpty(task.AgentId)) return; + + var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, task.AgentId); + if (!Directory.Exists(agentDir)) return; + + var taskDir = Path.Combine(agentDir, "tasks"); + if (!Directory.Exists(taskDir)) + { + Directory.CreateDirectory(taskDir); + } + + var fileName = $"{Guid.NewGuid()}.liquid"; + var taskFile = Path.Combine(taskDir, fileName); + + var model = new AgentTaskFileModel + { + Name = task.Name, + Description = task.Description, + Enabled = task.Enabled, + CreatedDateTime = DateTime.UtcNow, + UpdatedDateTime = DateTime.UtcNow + }; + + var prefix = "#metadata"; + var postfix = "/metadata"; + var fileContent = $"{prefix}\n{JsonSerializer.Serialize(model, _options)}\n{postfix}\n\n{task.Content}"; + File.WriteAllText(taskFile, fileContent); + } + + public void UpdateAgentTask() + { + } + + public bool DeleteAgentTask(string agentId, string taskId) + { + var agentDir = Path.Combine(_dbSettings.FileRepository, _agentSettings.DataDir, agentId); + if (!Directory.Exists(agentDir)) return false; + + var taskDir = Path.Combine(agentDir, "tasks"); + if (!Directory.Exists(taskDir)) return false; + + var taskFile = Directory.GetFiles(taskDir).FirstOrDefault(file => + { + var fileName = file.Split(Path.DirectorySeparatorChar).Last(); + var id = fileName.Split('.').First(); + return id.IsEqualTo(taskId); + }); + + if (string.IsNullOrWhiteSpace(taskFile)) return false; + + File.Delete(taskFile); + return true; + } + #endregion +} + +internal class AgentTaskFileModel +{ + public string Name { get; set; } + public string? Description { get; set; } + public bool Enabled { get; set; } + public DateTime CreatedDateTime { get; set; } + public DateTime UpdatedDateTime { get; set; } +} diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 8131bdd2..36eaa3ed 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -35,9 +35,9 @@ namespace BotSharp.Core.Repository { Key = x.Key, Values = new List - { - new StateValue { Data = x.Value, UpdateTime = DateTime.UtcNow } - } + { + new StateValue { Data = x.Value, UpdateTime = DateTime.UtcNow } + } }).ToList(); File.WriteAllText(stateFile, JsonSerializer.Serialize(initialStates, _options)); } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs index 892fb923..7273acb7 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.cs @@ -119,8 +119,7 @@ public partial class FileRepository : IBotSharpRepository .SetFunctions(FetchFunctions(d)) .SetTemplates(FetchTemplates(d)) .SetResponses(FetchResponses(d)) - .SetSamples(FetchSamples(d)) - .SetTasks(FetchTasks(d)); + .SetSamples(FetchSamples(d)); _agents.Add(agent); } } @@ -236,19 +235,10 @@ public partial class FileRepository : IBotSharpRepository foreach (var file in Directory.GetFiles(taskDir)) { - var fileName = file.Split(Path.DirectorySeparatorChar).Last(); - var id = fileName.Split('.').First(); - var data = File.ReadAllText(file); - var metadata = Regex.Match(data, @"#metadata.+/metadata", RegexOptions.Singleline); + var task = ParseAgentTask(file); + if (task == null) continue; - if (metadata.Success) - { - var task = metadata.Value.JsonContent(); - task.Id = id; - var content = Regex.Match(data, @"/metadata.+", RegexOptions.Singleline).Value; - task.Content = content.Substring(9).Trim(); - tasks.Add(task); - } + tasks.Add(task); } return tasks; @@ -273,6 +263,49 @@ public partial class FileRepository : IBotSharpRepository return responses; } + private Agent? ParseAgent(string agentDir) + { + if (string.IsNullOrEmpty(agentDir)) return null; + + var agentJson = File.ReadAllText(Path.Combine(agentDir, AGENT_FILE)); + if (string.IsNullOrEmpty(agentJson)) return null; + + var agent = JsonSerializer.Deserialize(agentJson, _options); + if (agent == null) return null; + + var instruction = FetchInstruction(agentDir); + var functions = FetchFunctions(agentDir); + var samples = FetchSamples(agentDir); + var templates = FetchTemplates(agentDir); + var responses = FetchResponses(agentDir); + + return agent.SetInstruction(instruction) + .SetFunctions(functions) + .SetTemplates(templates) + .SetSamples(samples) + .SetResponses(responses); + } + + private AgentTask? ParseAgentTask(string taskFile) + { + if (string.IsNullOrWhiteSpace(taskFile)) return null; + + var fileName = taskFile.Split(Path.DirectorySeparatorChar).Last(); + var id = fileName.Split('.').First(); + var data = File.ReadAllText(taskFile); + var metaData = Regex.Match(data, @"#metadata.+/metadata", RegexOptions.Singleline); + + if (!metaData.Success) return null; + + var task = metaData.Value.JsonContent(); + if (task == null) return null; + + task.Id = id; + var content = Regex.Match(data, @"/metadata.+", RegexOptions.Singleline).Value; + task.Content = content.Substring(9).Trim(); + return task; + } + private string[] ParseFileNameByPath(string path, string separator = ".") { var name = path.Split(Path.DirectorySeparatorChar).Last(); diff --git a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs index aa5977ba..8323a957 100644 --- a/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs +++ b/src/Infrastructure/BotSharp.Core/Tasks/Services/AgentTaskService.cs @@ -1,3 +1,4 @@ +using BotSharp.Abstraction.Repositories; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Tasks; using BotSharp.Abstraction.Tasks.Models; @@ -14,23 +15,29 @@ public class AgentTaskService : IAgentTaskService public async Task> GetTasks(AgentTaskFilter filter) { - var agentService = _services.GetRequiredService(); - var agents = await agentService.GetAgents(new AgentFilter()); - var tasks = new List(); - foreach (var agent in agents.Items) - { - if (filter.AgentId != null && filter.AgentId != agent.Id) - { - continue; - } - agent.Tasks.ForEach(x => x.Agent = agent); - tasks.AddRange(agent.Tasks); - } + var db = _services.GetRequiredService(); + var pagedTasks = db.GetAgentTasks(filter); + return await Task.FromResult(pagedTasks); + } - return new PagedItems - { - Items = tasks.Skip(filter.Pager.Offset).Take(filter.Pager.Size), - Count = tasks.Count, - }; + public async Task GetTask(string agentId, string taskId) + { + var db = _services.GetRequiredService(); + var task = db.GetAgentTask(agentId, taskId); + return await Task.FromResult(task); + } + + public async Task CreateTask(AgentTask task) + { + var db = _services.GetRequiredService(); + db.InsertAgentTask(task); + await Task.CompletedTask; + } + + public async Task DeleteTask(string agentId, string taskId) + { + var db = _services.GetRequiredService(); + var isDeleted = db.DeleteAgentTask(agentId, taskId); + return await Task.FromResult(isDeleted); } } diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentTaskController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentTaskController.cs index 57e0d825..0f858f21 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentTaskController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentTaskController.cs @@ -1,6 +1,4 @@ -using BotSharp.Abstraction.Agents.Models; using BotSharp.Abstraction.Tasks; -using BotSharp.Abstraction.Tasks.Models; namespace BotSharp.OpenAPI.Controllers; @@ -8,30 +6,52 @@ namespace BotSharp.OpenAPI.Controllers; [ApiController] public class AgentTaskController : ControllerBase { - private readonly IAgentService _agentService; + private readonly IAgentTaskService _agentTaskService; private readonly IServiceProvider _services; - public AgentTaskController(IAgentService agentService, IServiceProvider services) + public AgentTaskController(IAgentTaskService agentTaskService, IServiceProvider services) { - _agentService = agentService; + _agentTaskService = agentTaskService; _services = services; } - [HttpGet("/agent/task/{id}")] - public async Task GetAgentTask([FromRoute] string id) + [HttpGet("/agent/{agentId}/task/{taskId}")] + public async Task GetAgentTask([FromRoute] string agentId, [FromRoute] string taskId) { - throw new NotImplementedException(""); + var task = await _agentTaskService.GetTask(agentId, taskId); + if (task == null) return null; + + return AgentTaskViewModel.From(task); } [HttpGet("/agent/tasks")] public async Task> GetAgents([FromQuery] AgentTaskFilter filter) { - var taskService = _services.GetRequiredService(); - var tasks = await taskService.GetTasks(filter); + var tasks = await _agentTaskService.GetTasks(filter); return new PagedItems { Items = tasks.Items.Select(x => AgentTaskViewModel.From(x)), Count = tasks.Count }; } + + [HttpPost("/agent/{agentId}/task")] + public async Task CreateAgentTask([FromRoute] string agentId, [FromBody] AgentTaskCreateModel task) + { + var agentTask = task.ToAgentTask(); + agentTask.AgentId = agentId; + await _agentTaskService.CreateTask(agentTask); + } + + [HttpPut("/agent/{agentId}/task/{taskId}")] + public async Task UpdateAgentTask([FromRoute] string agentId, [FromRoute] string taskId) + { + throw new NotImplementedException(""); + } + + [HttpDelete("/agent/{agentId}/task/{taskId}")] + public async Task DeleteAgentTask([FromRoute] string agentId, [FromRoute] string taskId) + { + return await _agentTaskService.DeleteTask(agentId, taskId); + } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskCreateModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskCreateModel.cs new file mode 100644 index 00000000..89565e9f --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Agents/AgentTaskCreateModel.cs @@ -0,0 +1,22 @@ +using BotSharp.Abstraction.Tasks.Models; + +namespace BotSharp.OpenAPI.ViewModels.Agents; + +public class AgentTaskCreateModel +{ + public string Name { get; set; } + public string? Description { get; set; } + public string Content { get; set; } + public bool Enabled { get; set; } + + public AgentTask ToAgentTask() + { + return new AgentTask + { + Name = Name, + Description = Description, + Content = Content, + Enabled = Enabled + }; + } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentTaskDocument.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentTaskDocument.cs new file mode 100644 index 00000000..198b70c1 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Collections/AgentTaskDocument.cs @@ -0,0 +1,12 @@ +namespace BotSharp.Plugin.MongoStorage.Collections; + +public class AgentTaskDocument : MongoBase +{ + public string Name { get; set; } + public string? Description { get; set; } + public string Content { get; set; } + public bool Enabled { get; set; } + public string AgentId { get; set; } + public DateTime CreatedTime { get; set; } + public DateTime UpdatedTime { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs index a3e994b4..0067f885 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/MongoDbContext.cs @@ -31,6 +31,9 @@ public class MongoDbContext public IMongoCollection Agents => Database.GetCollection($"{_collectionPrefix}_Agents"); + public IMongoCollection AgentTasks + => Database.GetCollection($"{_collectionPrefix}_AgentTasks"); + public IMongoCollection Conversations => Database.GetCollection($"{_collectionPrefix}_Conversations"); diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs index 58fe44ff..dd4f80e4 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Agent.cs @@ -1,9 +1,11 @@ using BotSharp.Abstraction.Agents.Models; +using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Evaluations.Settings; using BotSharp.Abstraction.Functions.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Routing.Models; using BotSharp.Abstraction.Routing.Settings; +using BotSharp.Abstraction.Tasks.Models; using BotSharp.Plugin.MongoStorage.Collections; using BotSharp.Plugin.MongoStorage.Models; @@ -261,33 +263,7 @@ public partial class MongoRepository var agent = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == agentId); if (agent == null) return null; - return new Agent - { - Id = agent.Id, - Name = agent.Name, - IconUrl = agent.IconUrl, - Description = agent.Description, - Instruction = agent.Instruction, - Templates = !agent.Templates.IsNullOrEmpty() ? agent.Templates - .Select(t => AgentTemplateMongoElement.ToDomainElement(t)) - .ToList() : new List(), - Functions = !agent.Functions.IsNullOrEmpty() ? agent.Functions - .Select(f => FunctionDefMongoElement.ToDomainElement(f)) - .ToList() : new List(), - Responses = !agent.Responses.IsNullOrEmpty() ? agent.Responses - .Select(r => AgentResponseMongoElement.ToDomainElement(r)) - .ToList() : new List(), - Samples = agent.Samples ?? new List(), - IsPublic = agent.IsPublic, - Disabled = agent.Disabled, - Type = agent.Type, - InheritAgentId = agent.InheritAgentId, - Profiles = agent.Profiles, - RoutingRules = !agent.RoutingRules.IsNullOrEmpty() ? agent.RoutingRules - .Select(r => RoutingRuleMongoElement.ToDomainElement(agent.Id, agent.Name, r)) - .ToList() : new List(), - LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agent.LlmConfig) - }; + return TransformAgentDocument(agent); } public List GetAgents(AgentFilter filter) @@ -323,33 +299,7 @@ public partial class MongoRepository var agentDocs = _dc.Agents.Find(builder.And(filters)).ToList(); - return agentDocs.Select(x => new Agent - { - Id = x.Id, - Name = x.Name, - IconUrl = x.IconUrl, - Description = x.Description, - Instruction = x.Instruction, - Templates = !x.Templates.IsNullOrEmpty() ? x.Templates - .Select(t => AgentTemplateMongoElement.ToDomainElement(t)) - .ToList() : new List(), - Functions = !x.Functions.IsNullOrEmpty() ? x.Functions - .Select(f => FunctionDefMongoElement.ToDomainElement(f)) - .ToList() : new List(), - Responses = !x.Responses.IsNullOrEmpty() ? x.Responses - .Select(r => AgentResponseMongoElement.ToDomainElement(r)) - .ToList() : new List(), - Samples = x.Samples ?? new List(), - IsPublic = x.IsPublic, - Disabled = x.Disabled, - Type = x.Type, - InheritAgentId = x.InheritAgentId, - Profiles = x.Profiles, - RoutingRules = !x.RoutingRules.IsNullOrEmpty() ? x.RoutingRules - .Select(r => RoutingRuleMongoElement.ToDomainElement(x.Id, x.Name, r)) - .ToList() : new List(), - LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(x.LlmConfig) - }).ToList(); + return agentDocs.Select(x => TransformAgentDocument(x)).ToList(); } public List GetAgentsByUser(string userId) @@ -385,6 +335,100 @@ public partial class MongoRepository return agent.Templates?.FirstOrDefault(x => x.Name == templateName.ToLower())?.Content ?? string.Empty; } + + #region Task + public PagedItems GetAgentTasks(AgentTaskFilter filter) + { + var pager = filter.Pager ?? new Pagination(); + var builder = Builders.Filter; + var filters = new List>() { builder.Empty }; + + if (!string.IsNullOrEmpty(filter.AgentId)) + { + filters.Add(builder.Eq(x => x.AgentId, filter.AgentId)); + } + + var filterDef = builder.And(filters); + var sortDef = Builders.Sort.Descending(x => x.CreatedTime); + var totalTasks = _dc.AgentTasks.CountDocuments(filterDef); + var taskDocs = _dc.AgentTasks.Find(filterDef).Sort(sortDef).Skip(pager.Offset).Limit(pager.Size).ToList(); + + var agentIds = taskDocs.Select(x => x.AgentId).Distinct().ToList(); + var agents = GetAgents(new AgentFilter { AgentIds = agentIds }); + + var tasks = taskDocs.Select(x => new AgentTask + { + Id = x.Id, + Name = x.Name, + Description = x.Description, + Enabled = x.Enabled, + AgentId = x.AgentId, + Content = x.Content, + CreatedDateTime = x.CreatedTime, + UpdatedDateTime = x.UpdatedTime, + Agent = agents.FirstOrDefault(a => a.Id == x.AgentId) + }).ToList(); + + return new PagedItems + { + Items = tasks, + Count = (int)totalTasks + }; + } + + public AgentTask? GetAgentTask(string agentId, string taskId) + { + if (string.IsNullOrEmpty(taskId)) return null; + + var taskDoc = _dc.AgentTasks.AsQueryable().FirstOrDefault(x => x.Id == taskId); + if (taskDoc == null) return null; + + var agentDoc = _dc.Agents.AsQueryable().FirstOrDefault(x => x.Id == taskDoc.AgentId); + var agent = TransformAgentDocument(agentDoc); + + var task = new AgentTask + { + Id = taskDoc.Id, + Name = taskDoc.Name, + Description = taskDoc.Description, + Enabled = taskDoc.Enabled, + AgentId = taskDoc.AgentId, + Content = taskDoc.Content, + CreatedDateTime = taskDoc.CreatedTime, + UpdatedDateTime = taskDoc.UpdatedTime, + Agent = agent + }; + + return task; + } + + public void InsertAgentTask(AgentTask task) + { + var taskDoc = new AgentTaskDocument + { + Id = Guid.NewGuid().ToString(), + Name = task.Name, + Description = task.Description, + Enabled = task.Enabled, + AgentId = task.AgentId, + Content = task.Content, + CreatedTime = DateTime.UtcNow, + UpdatedTime = DateTime.UtcNow + }; + + _dc.AgentTasks.InsertOne(taskDoc); + } + + public bool DeleteAgentTask(string agentId, string taskId) + { + if (string.IsNullOrEmpty(taskId)) return false; + + var filter = Builders.Filter.Eq(x => x.Id, taskId); + var taskDeleted = _dc.AgentTasks.DeleteOne(filter); + return taskDeleted.DeletedCount > 0; + } + #endregion + public void BulkInsertAgents(List agents) { if (agents.IsNullOrEmpty()) return; @@ -453,4 +497,37 @@ public partial class MongoRepository } } + + private Agent TransformAgentDocument(AgentDocument? agentDoc) + { + if (agentDoc == null) return new Agent(); + + return new Agent + { + Id = agentDoc.Id, + Name = agentDoc.Name, + IconUrl = agentDoc.IconUrl, + Description = agentDoc.Description, + Instruction = agentDoc.Instruction, + Templates = !agentDoc.Templates.IsNullOrEmpty() ? agentDoc.Templates + .Select(t => AgentTemplateMongoElement.ToDomainElement(t)) + .ToList() : new List(), + Functions = !agentDoc.Functions.IsNullOrEmpty() ? agentDoc.Functions + .Select(f => FunctionDefMongoElement.ToDomainElement(f)) + .ToList() : new List(), + Responses = !agentDoc.Responses.IsNullOrEmpty() ? agentDoc.Responses + .Select(r => AgentResponseMongoElement.ToDomainElement(r)) + .ToList() : new List(), + Samples = agentDoc.Samples ?? new List(), + IsPublic = agentDoc.IsPublic, + Disabled = agentDoc.Disabled, + Type = agentDoc.Type, + InheritAgentId = agentDoc.InheritAgentId, + Profiles = agentDoc.Profiles, + RoutingRules = !agentDoc.RoutingRules.IsNullOrEmpty() ? agentDoc.RoutingRules + .Select(r => RoutingRuleMongoElement.ToDomainElement(agentDoc.Id, agentDoc.Name, r)) + .ToList() : new List(), + LlmConfig = AgentLlmConfigMongoElement.ToDomainElement(agentDoc.LlmConfig) + }; + } } diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 0a1c2530..9f1b2a20 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -217,9 +217,9 @@ public partial class MongoRepository if (!string.IsNullOrEmpty(filter.UserId)) filters.Add(builder.Eq(x => x.UserId, filter.UserId)); var filterDef = builder.And(filters); - var sortDefinition = Builders.Sort.Descending(x => x.CreatedTime); + var sortDef = Builders.Sort.Descending(x => x.CreatedTime); var pager = filter?.Pager ?? new Pagination(); - var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDefinition).Skip(pager.Offset).Limit(pager.Size).ToList(); + var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDef).Skip(pager.Offset).Limit(pager.Size).ToList(); var count = _dc.Conversations.CountDocuments(filterDef); foreach (var conv in conversationDocs)