diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs index 73352892..b86e5916 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/IAgentService.cs @@ -13,6 +13,7 @@ public interface IAgentService Task RefreshAgents(); Task> GetAgents(AgentFilter filter); Task> GetAgentOptions(List? agentIds = null, bool byName = false); + Task> GetAgentUtilityOptions(); /// /// Load agent configurations and trigger hooks diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs index 6233dfa7..3dc5247d 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentUtility.cs @@ -26,8 +26,7 @@ public class AgentUtility public class UtilityItem { [JsonPropertyName("function_name")] - [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] - public string? FunctionName { get; set; } + public string FunctionName { get; set; } = null!; [JsonPropertyName("template_name")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -36,4 +35,8 @@ public class UtilityItem [JsonPropertyName("visibility_expression")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? VisibilityExpression { get; set; } + + [JsonPropertyName("description")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Description { get; set; } } \ No newline at end of file diff --git a/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Utility.cs b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Utility.cs new file mode 100644 index 00000000..7537c80a --- /dev/null +++ b/src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.Utility.cs @@ -0,0 +1,101 @@ +using BotSharp.Abstraction.Repositories.Settings; +using System.IO; + +namespace BotSharp.Core.Agents.Services; + +public partial class AgentService +{ + public async Task> GetAgentUtilityOptions() + { + var utilities = new List(); + var hooks = _services.GetServices(); + foreach (var hook in hooks) + { + hook.AddUtilities(utilities); + } + + utilities = utilities.Where(x => !string.IsNullOrWhiteSpace(x.Category) + && !string.IsNullOrWhiteSpace(x.Name) + && !x.Items.IsNullOrEmpty()).ToList(); + + var allItems = utilities.SelectMany(x => x.Items).ToList(); + var functionNames = allItems.Select(x => x.FunctionName).Distinct().ToList(); + var mapper = await GetAgentDocs(functionNames); + + allItems.ForEach(x => + { + if (mapper.ContainsKey(x.FunctionName)) + { + x.Description = mapper[x.FunctionName]; + } + }); + + return utilities; + } + + #region Private methods + private async ValueTask> GetAgentDocs(IEnumerable names) + { + var mapper = new Dictionary(); + if (names.IsNullOrEmpty()) + { + return mapper; + } + + var dir = GetAgentDocDir(BuiltInAgentId.UtilityAssistant); + if (string.IsNullOrEmpty(dir)) + { + return mapper; + } + + var matchDocs = Directory.GetFiles(dir, "*.md") + .Where(x => names.Contains(Path.GetFileNameWithoutExtension(x))) + .ToList(); + + if (matchDocs.IsNullOrEmpty()) + { + return mapper; + } + + await foreach (var item in GetUtilityDescriptions(matchDocs)) + { + mapper[item.Key] = item.Value; + } + + return mapper; + } + + private string GetAgentDocDir(string agentId) + { + var dbSettings = _services.GetRequiredService(); + var agentSettings = _services.GetRequiredService(); + var dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, dbSettings.FileRepository, agentSettings.DataDir, agentId, "docs"); + if (!Directory.Exists(dir)) + { + dir = string.Empty; + } + return dir; + } + + private async IAsyncEnumerable> GetUtilityDescriptions(IEnumerable docs) + { + foreach (var doc in docs) + { + var content = string.Empty; + try + { + content = await File.ReadAllTextAsync(doc); + } + catch { } + + if (string.IsNullOrWhiteSpace(content)) + { + continue; + } + + var fileName = Path.GetFileNameWithoutExtension(doc); + yield return new KeyValuePair(fileName, content); + } + } + #endregion +} diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs index 0e53c746..2b2a72f6 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/AgentController.cs @@ -159,17 +159,10 @@ public class AgentController : ControllerBase } [HttpGet("/agent/utility/options")] - public IEnumerable GetAgentUtilityOptions() + public async Task> GetAgentUtilityOptions() { - var utilities = new List(); - var hooks = _services.GetServices(); - foreach (var hook in hooks) - { - hook.AddUtilities(utilities); - } - return utilities.Where(x => !string.IsNullOrWhiteSpace(x.Category) - && !string.IsNullOrWhiteSpace(x.Name) - && !x.Items.IsNullOrEmpty()).ToList(); + var agentService = _services.GetRequiredService(); + return await agentService.GetAgentUtilityOptions(); } [HttpGet("/agent/labels")] diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs index be052a1e..4d6d442a 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentUtilityMongoElement.cs @@ -48,7 +48,7 @@ public class AgentUtilityMongoElement public class AgentUtilityItemMongoElement { - public string? FunctionName { get; set; } + public string FunctionName { get; set; } public string? TemplateName { get; set; } public string? VisibilityExpression { get; set; } } \ No newline at end of file diff --git a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs index afaf2899..50d34701 100644 --- a/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs +++ b/tests/BotSharp.LLM.Tests/Core/TestAgentService.cs @@ -6,7 +6,6 @@ using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Plugins.Models; using BotSharp.Abstraction.Repositories.Filters; using BotSharp.Abstraction.Utilities; -using NetTopologySuite.Algorithm; namespace BotSharp.Plugin.Google.Core { @@ -111,5 +110,10 @@ namespace BotSharp.Plugin.Google.Core { return new PluginDef(); } + + public Task> GetAgentUtilityOptions() + { + return Task.FromResult(Enumerable.Empty()); + } } } \ No newline at end of file